Skip to content

Commit faf6d3c

Browse files
committed
Basic sample app
Change-Id: If381ab96faa6684a38de7316f6a825abe0f40625
1 parent fcc57f1 commit faf6d3c

File tree

6 files changed

+282
-14
lines changed

6 files changed

+282
-14
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<manifest
3-
xmlns:android="http://schemas.android.com/apk/res/android"
4-
xmlns:tools="http://schemas.android.com/tools"
5-
package="com.firebase.uidemo">
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
4+
package="com.firebase.uidemo">
65

76
<uses-permission android:name="android.permission.INTERNET" />
87
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@@ -17,7 +16,6 @@
1716
android:supportsRtl="true"
1817
android:theme="@style/AppTheme"
1918
tools:ignore="GoogleAppIndexingWarning">
20-
2119
<activity android:name=".ChooserActivity">
2220
<intent-filter>
2321
<action android:name="android.intent.action.MAIN" />
@@ -37,6 +35,9 @@
3735
<activity
3836
android:name=".auth.SignedInActivity"
3937
android:label="@string/title_auth_activity" />
38+
<activity
39+
android:name=".auth.AccountLinkActivity"
40+
android:label="@string/title_account_link"/>
4041

4142
<!-- Firestore demo -->
4243
<activity
@@ -55,7 +56,6 @@
5556
<activity
5657
android:name=".storage.ImageActivity"
5758
android:label="@string/title_storage_activity" />
58-
5959
</application>
6060

6161
</manifest>

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

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

28+
import com.firebase.uidemo.auth.AccountLinkActivity;
2829
import com.firebase.uidemo.auth.AuthUiActivity;
2930
import com.firebase.uidemo.database.firestore.FirestoreChatActivity;
3031
import com.firebase.uidemo.database.realtime.RealtimeDbChatActivity;
@@ -51,20 +52,23 @@ protected void onCreate(Bundle savedInstanceState) {
5152
private static class ActivityChooserAdapter extends RecyclerView.Adapter<ActivityStarterHolder> {
5253
private static final Class[] CLASSES = new Class[]{
5354
AuthUiActivity.class,
55+
AccountLinkActivity.class,
5456
FirestoreChatActivity.class,
5557
RealtimeDbChatActivity.class,
5658
ImageActivity.class,
5759
};
5860

5961
private static final int[] DESCRIPTION_NAMES = new int[]{
6062
R.string.title_auth_activity,
63+
R.string.title_account_link,
6164
R.string.title_firestore_activity,
6265
R.string.title_realtime_database_activity,
6366
R.string.title_storage_activity
6467
};
6568

6669
private static final int[] DESCRIPTION_IDS = new int[]{
6770
R.string.desc_auth,
71+
R.string.desc_account_link,
6872
R.string.desc_firestore,
6973
R.string.desc_realtime_database,
7074
R.string.desc_storage
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
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.google.android.gms.tasks.OnCompleteListener;
18+
import com.google.android.gms.tasks.Task;
19+
import com.google.firebase.auth.AuthCredential;
20+
import com.google.firebase.auth.AuthResult;
21+
import com.google.firebase.auth.FirebaseAuth;
22+
import com.google.firebase.auth.FirebaseUser;
23+
24+
import java.util.Arrays;
25+
import java.util.List;
26+
27+
import butterknife.BindView;
28+
import butterknife.ButterKnife;
29+
import butterknife.OnClick;
30+
31+
public class AccountLinkActivity 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_account_link);
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 = Arrays.asList(
82+
new AuthUI.IdpConfig.EmailBuilder().build(),
83+
new AuthUI.IdpConfig.PhoneBuilder().build(),
84+
new AuthUI.IdpConfig.GoogleBuilder().build());
85+
86+
Intent intent = AuthUI.getInstance().createSignInIntentBuilder()
87+
.setAvailableProviders(providers)
88+
.setIsSmartLockEnabled(false)
89+
.setUpgradeAnonymousAccounts(true)
90+
.build();
91+
92+
startActivityForResult(intent, RC_SIGN_IN);
93+
}
94+
95+
@OnClick(R.id.resolve_merge)
96+
public void resolveMerge() {
97+
if (mPendingCredential == null) {
98+
Toast.makeText(this, "Nothing to resolve.", Toast.LENGTH_SHORT).show();
99+
return;
100+
}
101+
102+
// TODO: Show how to do good data moving
103+
104+
FirebaseAuth.getInstance().signInWithCredential(mPendingCredential)
105+
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
106+
@Override
107+
public void onComplete(@NonNull Task<AuthResult> task) {
108+
mPendingCredential = null;
109+
updateUI();
110+
111+
if (task.isSuccessful()) {
112+
setStatus("Signed in as " + getUserIdentifier(task.getResult().getUser()));
113+
} else {
114+
Log.w(TAG, "Merge failed", task.getException());
115+
setStatus("Failed to resolve merge conflict, see logs.");
116+
}
117+
}
118+
});
119+
}
120+
121+
@OnClick(R.id.sign_out)
122+
public void signOut() {
123+
AuthUI.getInstance().signOut(this)
124+
.addOnCompleteListener(new OnCompleteListener<Void>() {
125+
@Override
126+
public void onComplete(@NonNull Task<Void> task) {
127+
updateUI();
128+
}
129+
});
130+
}
131+
132+
@Override
133+
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
134+
super.onActivityResult(requestCode, resultCode, data);
135+
Log.d(TAG, "onActivityResult: " + requestCode + ", " + resultCode + ", " + data);
136+
137+
if (requestCode == RC_SIGN_IN) {
138+
IdpResponse response = IdpResponse.fromResultIntent(data);
139+
if (resultCode == RESULT_OK) {
140+
setStatus("Signed in as " + getUserIdentifier(FirebaseAuth.getInstance().getCurrentUser()));
141+
} else {
142+
if (response.getError().getErrorCode() == ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT) {
143+
setStatus("Merge conflict: user already exists.");
144+
mResolveMergeButton.setEnabled(true);
145+
mPendingCredential = response.getPendingCredential();
146+
}
147+
}
148+
149+
updateUI();
150+
}
151+
}
152+
153+
private void updateUI() {
154+
FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
155+
156+
if (currentUser == null) {
157+
// Not signed in
158+
mAnonSignInButton.setEnabled(true);
159+
mLaunchUIButton.setEnabled(false);
160+
mResolveMergeButton.setEnabled(false);
161+
mSignOutButton.setEnabled(false);
162+
} else if (mPendingCredential == null && currentUser.isAnonymous()) {
163+
// Anonymous user, waiting for linking
164+
mAnonSignInButton.setEnabled(false);
165+
mLaunchUIButton.setEnabled(true);
166+
mResolveMergeButton.setEnabled(false);
167+
mSignOutButton.setEnabled(true);
168+
} else if (mPendingCredential == null && !currentUser.isAnonymous()) {
169+
// Fully signed in
170+
mAnonSignInButton.setEnabled(false);
171+
mLaunchUIButton.setEnabled(false);
172+
mResolveMergeButton.setEnabled(false);
173+
mSignOutButton.setEnabled(true);
174+
} else if (mPendingCredential != null) {
175+
// Signed in anonymous, awaiting merge conflict
176+
mAnonSignInButton.setEnabled(false);
177+
mLaunchUIButton.setEnabled(false);
178+
mResolveMergeButton.setEnabled(true);
179+
mSignOutButton.setEnabled(true);
180+
}
181+
}
182+
183+
private void setStatus(String message) {
184+
mStatus.setText(message);
185+
}
186+
187+
private String getUserIdentifier(FirebaseUser user) {
188+
if (user.isAnonymous()) {
189+
return user.getUid();
190+
} else if (!TextUtils.isEmpty(user.getEmail())) {
191+
return user.getEmail();
192+
} else if (!TextUtils.isEmpty(user.getPhoneNumber())) {
193+
return user.getPhoneNumber();
194+
} else {
195+
return "unknown";
196+
}
197+
}
198+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<LinearLayout
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:id="@+id/root"
6+
android:layout_width="match_parent"
7+
android:layout_height="match_parent"
8+
android:layout_marginLeft="24dp"
9+
android:layout_marginRight="24dp"
10+
android:layout_marginTop="16dp"
11+
android:layout_marginBottom="16dp"
12+
android:orientation="vertical"
13+
tools:context=".auth.AuthUiActivity">
14+
15+
16+
<TextView
17+
style="@style/Base.TextAppearance.AppCompat.Headline"
18+
android:layout_width="wrap_content"
19+
android:layout_height="wrap_content"
20+
android:layout_gravity="center_horizontal"
21+
android:drawableTop="@drawable/firebase_auth_120dp"
22+
android:text="FirebaseUI Account Linking" />
23+
24+
<TextView
25+
android:id="@+id/status_text"
26+
android:layout_width="match_parent"
27+
android:layout_height="wrap_content"
28+
android:layout_margin="16dp"
29+
android:gravity="center"
30+
tools:text="This is the status view, sometimes it will have a very long status and other..." />
31+
32+
<Button
33+
android:id="@+id/anon_sign_in"
34+
style="@style/Widget.AppCompat.Button.Colored"
35+
android:layout_width="200dp"
36+
android:layout_height="wrap_content"
37+
android:layout_gravity="center"
38+
android:text="Anonymous Sign In" />
39+
40+
<Button
41+
android:id="@+id/begin_flow"
42+
style="@style/Widget.AppCompat.Button.Colored"
43+
android:layout_width="200dp"
44+
android:layout_height="wrap_content"
45+
android:layout_gravity="center"
46+
android:text="Launch Auth UI"
47+
android:enabled="false" />
48+
49+
<Button
50+
android:id="@+id/resolve_merge"
51+
style="@style/Widget.AppCompat.Button.Colored"
52+
android:layout_width="200dp"
53+
android:layout_height="wrap_content"
54+
android:layout_gravity="center"
55+
android:text="Resolve Merge Conflict"
56+
android:enabled="false" />
57+
58+
<Button
59+
android:id="@+id/sign_out"
60+
style="@style/Widget.AppCompat.Button.Colored"
61+
android:layout_width="200dp"
62+
android:layout_height="wrap_content"
63+
android:layout_gravity="center"
64+
android:text="Sign Out"
65+
android:enabled="false" />
66+
67+
</LinearLayout>

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33

44
<!-- Chooser -->
55
<string name="title_auth_activity">Auth UI demo</string>
6+
<string name="title_account_link">Auth UI Account Linking</string>
67
<string name="title_firestore_activity">Cloud Firestore Demo</string>
78
<string name="title_realtime_database_activity">Real-time database demo</string>
89
<string name="title_storage_activity">Storage Image Demo</string>
910

1011
<string name="desc_auth">Demonstrates the Firebase Auth UI flow, with customization options.</string>
12+
<string name="desc_account_link">Demonstrates upgrading an anonymous account using FirebaseUI.</string>
1113
<string name="desc_firestore">Demonstrates using a FirestoreRecyclerAdapter to load data from Cloud Firestore into a RecyclerView for a basic chat app.</string>
1214
<string name="desc_realtime_database">Demonstrates using a FirebaseRecyclerAdapter to load data from Firebase Database into a RecyclerView for a basic chat app.</string>
1315
<string name="desc_storage">Demonstrates displaying an image from Cloud Storage using Glide.</string>

auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailFragment.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@
4444
import com.google.firebase.auth.FirebaseAuthUserCollisionException;
4545
import com.google.firebase.auth.FirebaseAuthWeakPasswordException;
4646

47-
import static android.app.Activity.RESULT_CANCELED;
48-
4947
/**
5048
* Fragment to display an email/name/password sign up form for new users.
5149
*/
@@ -271,12 +269,11 @@ public void onSuccess(AuthResult authResult) {
271269
.addOnFailureListener(getActivity(), new OnFailureListener() {
272270
@Override
273271
public void onFailure(@NonNull Exception e) {
274-
if (AnonymousUpgradeUtils.isUpgradeFailure(getFlowParams(), getFirebaseAuth(), e)) {
275-
// Anonymous collision
276-
IdpResponse res = new IdpResponse.Builder(EmailAuthProvider.getCredential(email, password))
277-
.build();
278-
finish(RESULT_CANCELED, res.toIntent());
279-
} else if (e instanceof FirebaseAuthWeakPasswordException) {
272+
// NOTE: there is no need to check for a linking failure here because
273+
// this Fragment only handles new email registrations, the check
274+
// to ensure that the email is unique happens before this screen.
275+
276+
if (e instanceof FirebaseAuthWeakPasswordException) {
280277
// Password too weak
281278
mPasswordInput.setError(getResources().getQuantityString(
282279
R.plurals.fui_error_weak_password, R.integer.fui_min_password_length));

0 commit comments

Comments
 (0)