Skip to content

Commit a97133b

Browse files
lsiracsamtstern
authored andcommitted
Upgrading anonymous accounts (#1368)
1 parent c228ef1 commit a97133b

39 files changed

+1700
-181
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
<activity
3838
android:name=".auth.SignedInActivity"
3939
android:label="@string/title_auth_activity" />
40+
<activity
41+
android:name=".auth.AnonymousUpgradeActivity"
42+
android:label="@string/title_anonymous_upgrade"/>
4043

4144
<!-- Firestore demo -->
4245
<activity

app/src/main/java/com/firebase/uidemo/ChooserActivity.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import android.view.ViewGroup;
2727
import android.widget.TextView;
2828

29+
import com.firebase.uidemo.auth.AnonymousUpgradeActivity;
2930
import com.firebase.uidemo.auth.AuthUiActivity;
3031
import com.firebase.uidemo.database.firestore.FirestoreChatActivity;
3132
import com.firebase.uidemo.database.firestore.FirestorePagingActivity;
@@ -53,6 +54,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
5354
private static class ActivityChooserAdapter extends RecyclerView.Adapter<ActivityStarterHolder> {
5455
private static final Class[] CLASSES = new Class[]{
5556
AuthUiActivity.class,
57+
AnonymousUpgradeActivity.class,
5658
FirestoreChatActivity.class,
5759
FirestorePagingActivity.class,
5860
RealtimeDbChatActivity.class,
@@ -61,6 +63,7 @@ private static class ActivityChooserAdapter extends RecyclerView.Adapter<Activit
6163

6264
private static final int[] DESCRIPTION_NAMES = new int[]{
6365
R.string.title_auth_activity,
66+
R.string.title_anonymous_upgrade,
6467
R.string.title_firestore_activity,
6568
R.string.title_firestore_paging_activity,
6669
R.string.title_realtime_database_activity,
@@ -69,6 +72,7 @@ private static class ActivityChooserAdapter extends RecyclerView.Adapter<Activit
6972

7073
private static final int[] DESCRIPTION_IDS = new int[]{
7174
R.string.desc_auth,
75+
R.string.desc_anonymous_upgrade,
7276
R.string.desc_firestore,
7377
R.string.desc_firestore_paging,
7478
R.string.desc_realtime_database,
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
package com.firebase.uidemo.auth;
2+
3+
import android.content.Intent;
4+
import android.os.Bundle;
5+
import android.support.annotation.NonNull;
6+
import android.support.v7.app.AppCompatActivity;
7+
import android.text.TextUtils;
8+
import android.util.Log;
9+
import android.widget.Button;
10+
import android.widget.TextView;
11+
import android.widget.Toast;
12+
13+
import com.firebase.ui.auth.AuthUI;
14+
import com.firebase.ui.auth.ErrorCodes;
15+
import com.firebase.ui.auth.IdpResponse;
16+
import com.firebase.uidemo.R;
17+
import com.firebase.uidemo.util.ConfigurationUtils;
18+
import com.google.android.gms.tasks.OnCompleteListener;
19+
import com.google.android.gms.tasks.Task;
20+
import com.google.firebase.auth.AuthCredential;
21+
import com.google.firebase.auth.AuthResult;
22+
import com.google.firebase.auth.FirebaseAuth;
23+
import com.google.firebase.auth.FirebaseUser;
24+
25+
import java.util.List;
26+
27+
import butterknife.BindView;
28+
import butterknife.ButterKnife;
29+
import butterknife.OnClick;
30+
31+
public class AnonymousUpgradeActivity extends AppCompatActivity {
32+
33+
private static final String TAG = "AccountLink";
34+
35+
private static final int RC_SIGN_IN = 123;
36+
37+
@BindView(R.id.status_text)
38+
TextView mStatus;
39+
40+
@BindView(R.id.anon_sign_in)
41+
Button mAnonSignInButton;
42+
43+
@BindView(R.id.begin_flow)
44+
Button mLaunchUIButton;
45+
46+
@BindView(R.id.resolve_merge)
47+
Button mResolveMergeButton;
48+
49+
@BindView(R.id.sign_out)
50+
Button mSignOutButton;
51+
52+
private AuthCredential mPendingCredential;
53+
54+
@Override
55+
protected void onCreate(Bundle savedInstanceState) {
56+
super.onCreate(savedInstanceState);
57+
setContentView(R.layout.activity_anonymous_upgrade);
58+
ButterKnife.bind(this);
59+
}
60+
61+
@OnClick(R.id.anon_sign_in)
62+
public void signInAnonymously() {
63+
FirebaseAuth.getInstance().signInAnonymously()
64+
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
65+
@Override
66+
public void onComplete(@NonNull Task<AuthResult> task) {
67+
updateUI();
68+
69+
if (task.isSuccessful()) {
70+
setStatus("Signed in anonymously as user "
71+
+ getUserIdentifier(task.getResult().getUser()));
72+
} else {
73+
setStatus("Anonymous sign in failed.");
74+
}
75+
}
76+
});
77+
}
78+
79+
@OnClick(R.id.begin_flow)
80+
public void startAuthUI() {
81+
List<AuthUI.IdpConfig> providers = ConfigurationUtils.getConfiguredProviders(this);
82+
Intent intent = AuthUI.getInstance().createSignInIntentBuilder()
83+
.setLogo(R.drawable.firebase_auth_120dp)
84+
.setAvailableProviders(providers)
85+
.setIsSmartLockEnabled(false)
86+
.enableAnonymousUsersAutoUpgrade()
87+
.build();
88+
startActivityForResult(intent, RC_SIGN_IN);
89+
}
90+
91+
@OnClick(R.id.resolve_merge)
92+
public void resolveMerge() {
93+
if (mPendingCredential == null) {
94+
Toast.makeText(this, "Nothing to resolve.", Toast.LENGTH_SHORT).show();
95+
return;
96+
}
97+
98+
// TODO: Show how to do good data moving
99+
100+
FirebaseAuth.getInstance().signInWithCredential(mPendingCredential)
101+
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
102+
@Override
103+
public void onComplete(@NonNull Task<AuthResult> task) {
104+
mPendingCredential = null;
105+
updateUI();
106+
107+
if (task.isSuccessful()) {
108+
setStatus("Signed in as " + getUserIdentifier(task.getResult().getUser()));
109+
} else {
110+
Log.w(TAG, "Merge failed", task.getException());
111+
setStatus("Failed to resolve merge conflict, see logs.");
112+
}
113+
}
114+
});
115+
}
116+
117+
@OnClick(R.id.sign_out)
118+
public void signOut() {
119+
AuthUI.getInstance().signOut(this)
120+
.addOnCompleteListener(new OnCompleteListener<Void>() {
121+
@Override
122+
public void onComplete(@NonNull Task<Void> task) {
123+
setStatus(null);
124+
updateUI();
125+
}
126+
});
127+
}
128+
129+
@Override
130+
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
131+
super.onActivityResult(requestCode, resultCode, data);
132+
if (requestCode == RC_SIGN_IN) {
133+
IdpResponse response = IdpResponse.fromResultIntent(data);
134+
if (response == null) {
135+
// User pressed back button
136+
return;
137+
}
138+
if (resultCode == RESULT_OK) {
139+
setStatus("Signed in as " + getUserIdentifier(FirebaseAuth.getInstance().getCurrentUser()));
140+
} else {
141+
if (response.getError().getErrorCode() == ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT) {
142+
setStatus("Merge conflict: user already exists.");
143+
mResolveMergeButton.setEnabled(true);
144+
mPendingCredential = response.getCredentialForLinking();
145+
}
146+
}
147+
148+
updateUI();
149+
}
150+
}
151+
152+
private void updateUI() {
153+
FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
154+
155+
if (currentUser == null) {
156+
// Not signed in
157+
mAnonSignInButton.setEnabled(true);
158+
mLaunchUIButton.setEnabled(false);
159+
mResolveMergeButton.setEnabled(false);
160+
mSignOutButton.setEnabled(false);
161+
} else if (mPendingCredential == null && currentUser.isAnonymous()) {
162+
// Anonymous user, waiting for linking
163+
mAnonSignInButton.setEnabled(false);
164+
mLaunchUIButton.setEnabled(true);
165+
mResolveMergeButton.setEnabled(false);
166+
mSignOutButton.setEnabled(true);
167+
} else if (mPendingCredential == null && !currentUser.isAnonymous()) {
168+
// Fully signed in
169+
mAnonSignInButton.setEnabled(false);
170+
mLaunchUIButton.setEnabled(false);
171+
mResolveMergeButton.setEnabled(false);
172+
mSignOutButton.setEnabled(true);
173+
} else if (mPendingCredential != null) {
174+
// Signed in anonymous, awaiting merge conflict
175+
mAnonSignInButton.setEnabled(false);
176+
mLaunchUIButton.setEnabled(false);
177+
mResolveMergeButton.setEnabled(true);
178+
mSignOutButton.setEnabled(true);
179+
}
180+
}
181+
182+
private void setStatus(String message) {
183+
mStatus.setText(message);
184+
}
185+
186+
private String getUserIdentifier(FirebaseUser user) {
187+
if (user.isAnonymous()) {
188+
return user.getUid();
189+
} else if (!TextUtils.isEmpty(user.getEmail())) {
190+
return user.getEmail();
191+
} else if (!TextUtils.isEmpty(user.getPhoneNumber())) {
192+
return user.getPhoneNumber();
193+
} else {
194+
return "unknown";
195+
}
196+
}
197+
}

app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@
3838
import com.firebase.ui.auth.ErrorCodes;
3939
import com.firebase.ui.auth.IdpResponse;
4040
import com.firebase.uidemo.R;
41+
import com.firebase.uidemo.util.ConfigurationUtils;
4142
import com.google.android.gms.common.Scopes;
4243
import com.google.android.gms.tasks.OnCompleteListener;
4344
import com.google.android.gms.tasks.Task;
4445
import com.google.firebase.auth.AuthResult;
4546
import com.google.firebase.auth.FirebaseAuth;
4647

4748
import java.util.ArrayList;
48-
import java.util.Arrays;
4949
import java.util.List;
5050

5151
import butterknife.BindView;
@@ -113,7 +113,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
113113
setContentView(R.layout.auth_ui_layout);
114114
ButterKnife.bind(this);
115115

116-
if (isGoogleMisconfigured()) {
116+
if (ConfigurationUtils.isGoogleMisconfigured(this)) {
117117
mUseGoogleProvider.setChecked(false);
118118
mUseGoogleProvider.setEnabled(false);
119119
mUseGoogleProvider.setText(R.string.google_label_missing_config);
@@ -128,7 +128,7 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
128128
});
129129
}
130130

131-
if (isFacebookMisconfigured()) {
131+
if (ConfigurationUtils.isFacebookMisconfigured(this)) {
132132
mUseFacebookProvider.setChecked(false);
133133
mUseFacebookProvider.setEnabled(false);
134134
mUseFacebookProvider.setText(R.string.facebook_label_missing_config);
@@ -143,13 +143,13 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
143143
});
144144
}
145145

146-
if (isTwitterMisconfigured()) {
146+
if (ConfigurationUtils.isTwitterMisconfigured(this)) {
147147
mUseTwitterProvider.setChecked(false);
148148
mUseTwitterProvider.setEnabled(false);
149149
mUseTwitterProvider.setText(R.string.twitter_label_missing_config);
150150
}
151151

152-
if (isGitHubMisconfigured()) {
152+
if (ConfigurationUtils.isGitHubMisconfigured(this)) {
153153
mUseGitHubProvider.setChecked(false);
154154
mUseGitHubProvider.setEnabled(false);
155155
mUseGitHubProvider.setText(R.string.github_label_missing_config);
@@ -164,8 +164,10 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
164164
});
165165
}
166166

167-
if (isGoogleMisconfigured() || isFacebookMisconfigured()
168-
|| isTwitterMisconfigured() || isGitHubMisconfigured()) {
167+
if (ConfigurationUtils.isGoogleMisconfigured(this)
168+
|| ConfigurationUtils.isFacebookMisconfigured(this)
169+
|| ConfigurationUtils.isTwitterMisconfigured(this)
170+
|| ConfigurationUtils.isGitHubMisconfigured(this)) {
169171
showSnackbar(R.string.configuration_required);
170172
}
171173

@@ -223,7 +225,7 @@ protected void onResume() {
223225
}
224226

225227
private void handleSignInResponse(int resultCode, Intent data) {
226-
IdpResponse response = IdpResponse.fromResultIntent(data);
228+
final IdpResponse response = IdpResponse.fromResultIntent(data);
227229

228230
// Successfully signed in
229231
if (resultCode == RESULT_OK) {
@@ -336,33 +338,6 @@ private String getSelectedPrivacyPolicyUrl() {
336338
return FIREBASE_PRIVACY_POLICY_URL;
337339
}
338340

339-
private boolean isGoogleMisconfigured() {
340-
return AuthUI.UNCONFIGURED_CONFIG_VALUE.equals(getString(R.string.default_web_client_id));
341-
}
342-
343-
private boolean isFacebookMisconfigured() {
344-
return AuthUI.UNCONFIGURED_CONFIG_VALUE.equals(getString(R.string.facebook_application_id));
345-
}
346-
347-
private boolean isTwitterMisconfigured() {
348-
List<String> twitterConfigs = Arrays.asList(
349-
getString(R.string.twitter_consumer_key),
350-
getString(R.string.twitter_consumer_secret)
351-
);
352-
353-
return twitterConfigs.contains(AuthUI.UNCONFIGURED_CONFIG_VALUE);
354-
}
355-
356-
private boolean isGitHubMisconfigured() {
357-
List<String> gitHubConfigs = Arrays.asList(
358-
getString(R.string.firebase_web_host),
359-
getString(R.string.github_client_id),
360-
getString(R.string.github_client_secret)
361-
);
362-
363-
return gitHubConfigs.contains(AuthUI.UNCONFIGURED_CONFIG_VALUE);
364-
}
365-
366341
private void setGoogleScopesEnabled(boolean enabled) {
367342
mGoogleScopesHeader.setEnabled(enabled);
368343
mGoogleScopeDriveFile.setEnabled(enabled);

0 commit comments

Comments
 (0)