diff --git a/app/build.gradle b/app/build.gradle index 17a12e940..f077dd862 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -46,6 +46,9 @@ dependencies { compile 'pub.devrel:easypermissions:0.2.1' compile 'com.jakewharton:butterknife:8.4.0' apt 'com.jakewharton:butterknife-compiler:8.4.0' + debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' + releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' + testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' } apply plugin: 'com.google.gms.google-services' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 09e0ee7fc..124bf4d2b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + android:theme="@style/Theme.AppCompat.NoActionBar"/> + android:theme="@style/Theme.AppCompat.NoActionBar"/> - - - - delete(@NonNull Activity activity) { CredentialsApiHelper credentialHelper = CredentialsApiHelper.getInstance(gacHelper); // Get all SmartLock credentials associated with the user - List credentials = SmartLockUtil.credentialsFromFirebaseUser(firebaseUser); + List credentials = SmartLockBase.credentialsFromFirebaseUser(firebaseUser); // For each Credential in the list, create a task to delete it. List> credentialTasks = new ArrayList<>(); @@ -571,7 +569,8 @@ public SignInIntentBuilder setProviders(@NonNull List idpConfigs) { for (IdpConfig idpConfig : idpConfigs) { if (configuredProviders.contains(idpConfig.getProviderId())) { throw new IllegalArgumentException("Each provider can only be set once. " - + idpConfig.getProviderId() + " was set twice."); + + idpConfig.getProviderId() + + " was set twice."); } configuredProviders.add(idpConfig.getProviderId()); mProviders.add(idpConfig); @@ -613,24 +612,6 @@ public SignInIntentBuilder setIsSmartLockEnabled(boolean enabled) { return this; } - public Intent build() { - Context context = mApp.getApplicationContext(); - return build(context); - } - - @VisibleForTesting - public Intent build(Context context) { - return ChooseAccountActivity.createIntent( - context, - new FlowParameters( - mApp.getName(), - new ArrayList<>(mProviders), - mTheme, - mLogo, - mTosUrl, - mIsSmartLockEnabled)); - } - private boolean isIdpAlreadyConfigured(@NonNull String providerId) { for (IdpConfig config : mProviders) { if (config.getProviderId().equals(providerId)) { @@ -639,5 +620,19 @@ private boolean isIdpAlreadyConfigured(@NonNull String providerId) { } return false; } + + public Intent build() { + return KickoffActivity.createIntent(mApp.getApplicationContext(), getFlowParams()); + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public FlowParameters getFlowParams() { + return new FlowParameters(mApp.getName(), + new ArrayList<>(mProviders), + mTheme, + mLogo, + mTosUrl, + mIsSmartLockEnabled); + } } } diff --git a/auth/src/main/java/com/firebase/ui/auth/KickoffActivity.java b/auth/src/main/java/com/firebase/ui/auth/KickoffActivity.java new file mode 100644 index 000000000..d09fd514b --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/KickoffActivity.java @@ -0,0 +1,41 @@ +package com.firebase.ui.auth; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import com.firebase.ui.auth.ui.ActivityHelper; +import com.firebase.ui.auth.ui.AppCompatBase; +import com.firebase.ui.auth.ui.ExtraConstants; +import com.firebase.ui.auth.ui.FlowParameters; +import com.firebase.ui.auth.util.signincontainer.SignInDelegate; + +public class KickoffActivity extends AppCompatBase { + @Override + protected void onCreate(Bundle savedInstance) { + super.onCreate(savedInstance); + if (savedInstance == null) { + SignInDelegate.delegate(this, mActivityHelper.getFlowParams()); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + // It doesn't matter what we put here, we just don't want outState to be empty + outState.putBoolean(ExtraConstants.HAS_EXISTING_INSTANCE, true); + super.onSaveInstanceState(outState); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + SignInDelegate delegate = SignInDelegate.getInstance(this); + if (delegate != null) { + delegate.onActivityResult(requestCode, resultCode, data); + } + } + + public static Intent createIntent(Context context, FlowParameters flowParams) { + return ActivityHelper.createBaseIntent(context, KickoffActivity.class, flowParams); + } +} diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/AuthCredentialHelper.java b/auth/src/main/java/com/firebase/ui/auth/provider/AuthCredentialHelper.java similarity index 88% rename from auth/src/main/java/com/firebase/ui/auth/ui/AuthCredentialHelper.java rename to auth/src/main/java/com/firebase/ui/auth/provider/AuthCredentialHelper.java index c60568e2c..d65af6450 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/AuthCredentialHelper.java +++ b/auth/src/main/java/com/firebase/ui/auth/provider/AuthCredentialHelper.java @@ -12,14 +12,11 @@ * limitations under the License. */ -package com.firebase.ui.auth.ui; +package com.firebase.ui.auth.provider; import android.support.annotation.Nullable; -import com.firebase.ui.auth.provider.FacebookProvider; -import com.firebase.ui.auth.provider.GoogleProvider; import com.firebase.ui.auth.IdpResponse; -import com.firebase.ui.auth.provider.TwitterProvider; import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.FacebookAuthProvider; import com.google.firebase.auth.GoogleAuthProvider; diff --git a/auth/src/main/java/com/firebase/ui/auth/provider/GoogleProvider.java b/auth/src/main/java/com/firebase/ui/auth/provider/GoogleProvider.java index 4acbfe8fb..3ddc5d08b 100644 --- a/auth/src/main/java/com/firebase/ui/auth/provider/GoogleProvider.java +++ b/auth/src/main/java/com/firebase/ui/auth/provider/GoogleProvider.java @@ -29,6 +29,7 @@ import com.firebase.ui.auth.AuthUI.IdpConfig; import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.R; +import com.firebase.ui.auth.util.GoogleApiConstants; import com.google.android.gms.auth.api.Auth; import com.google.android.gms.auth.api.signin.GoogleSignInAccount; import com.google.android.gms.auth.api.signin.GoogleSignInOptions; @@ -43,7 +44,6 @@ public class GoogleProvider implements IdpProvider, OnClickListener, GoogleApiClient.OnConnectionFailedListener { private static final String TAG = "GoogleProvider"; - private static final int AUTO_MANAGE_ID = 1; private static final int RC_SIGN_IN = 20; private static final String ERROR_KEY = "error"; private GoogleApiClient mGoogleApiClient; @@ -56,12 +56,12 @@ public GoogleProvider(FragmentActivity activity, IdpConfig idpConfig) { public GoogleProvider(FragmentActivity activity, IdpConfig idpConfig, @Nullable String email) { mActivity = activity; - String mClientId = activity.getString(R.string.default_web_client_id); + String clientId = activity.getString(R.string.default_web_client_id); GoogleSignInOptions.Builder builder = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestEmail() - .requestIdToken(mClientId); + .requestIdToken(clientId); if (activity.getResources().getIdentifier( "google_permissions", "array", activity.getPackageName()) != 0) { @@ -80,7 +80,7 @@ public GoogleProvider(FragmentActivity activity, IdpConfig idpConfig, @Nullable } mGoogleApiClient = new GoogleApiClient.Builder(activity) - .enableAutoManage(activity, AUTO_MANAGE_ID, this) + .enableAutoManage(activity, GoogleApiConstants.AUTO_MANAGE_ID0, this) .addApi(Auth.GOOGLE_SIGN_IN_API, builder.build()) .build(); } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/ActivityBase.java b/auth/src/main/java/com/firebase/ui/auth/ui/ActivityBase.java deleted file mode 100644 index ccad5f1a1..000000000 --- a/auth/src/main/java/com/firebase/ui/auth/ui/ActivityBase.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.firebase.ui.auth.ui; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; - -public class ActivityBase extends Activity { - protected ActivityHelper mActivityHelper; - - @Override - protected void onCreate(Bundle savedInstance) { - super.onCreate(savedInstance); - mActivityHelper = new ActivityHelper(this, getIntent()); - mActivityHelper.configureTheme(); - } - - public void finish(int resultCode, Intent intent) { - mActivityHelper.finish(resultCode, intent); - } -} diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/ActivityHelper.java b/auth/src/main/java/com/firebase/ui/auth/ui/ActivityHelper.java index 13c491eb2..f3b9c9cf8 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/ActivityHelper.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/ActivityHelper.java @@ -14,57 +14,19 @@ package com.firebase.ui.auth.ui; -import android.app.Activity; -import android.app.ProgressDialog; -import android.content.Context; import android.content.Intent; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.annotation.StringRes; -import com.firebase.ui.auth.IdpResponse; -import com.firebase.ui.auth.util.SmartLock; -import com.google.android.gms.auth.api.Auth; -import com.google.android.gms.auth.api.credentials.CredentialsApi; -import com.google.firebase.FirebaseApp; -import com.google.firebase.auth.FirebaseAuth; +import com.firebase.ui.auth.util.signincontainer.SaveSmartLock; import com.google.firebase.auth.FirebaseUser; -import static android.app.Activity.RESULT_OK; -import static com.firebase.ui.auth.util.Preconditions.checkNotNull; +public class ActivityHelper extends BaseHelper { + private AppCompatBase mActivity; -public class ActivityHelper { - private ProgressDialog mProgressDialog; - private Activity mActivity; - private final FlowParameters mFlowParams; - - public ActivityHelper(Activity activity, Intent intent) { + public ActivityHelper(AppCompatBase activity, Intent intent) { + super(activity, (FlowParameters) intent.getParcelableExtra(ExtraConstants.EXTRA_FLOW_PARAMS)); mActivity = activity; - mFlowParams = intent.getParcelableExtra(ExtraConstants.EXTRA_FLOW_PARAMS); - } - - public void configureTheme() { - mActivity.setTheme(mFlowParams.themeId); - } - - public void dismissDialog() { - if (mProgressDialog != null && mProgressDialog.isShowing()) { - mProgressDialog.dismiss(); - mProgressDialog = null; - } - } - - public FlowParameters getFlowParams() { - return mFlowParams; - } - - public void showLoadingDialog(String message) { - dismissDialog(); - mProgressDialog = ProgressDialog.show(mActivity, "", message, true); - } - - public void showLoadingDialog(@StringRes int stringResource) { - showLoadingDialog(mActivity.getString(stringResource)); } public void startActivityForResult(Intent intent, int requestCode) { @@ -72,80 +34,17 @@ public void startActivityForResult(Intent intent, int requestCode) { } public void finish(int resultCode, Intent intent) { - mActivity.setResult(resultCode, intent); - mActivity.finish(); - } - - public Context getApplicationContext() { - return mActivity.getApplicationContext(); - } - - public String getAppName() { - return mFlowParams.appName; - } - - public FirebaseApp getFirebaseApp() { - return FirebaseApp.getInstance(mFlowParams.appName); - } - - public FirebaseAuth getFirebaseAuth() { - return FirebaseAuth.getInstance(getFirebaseApp()); - } - - public CredentialsApi getCredentialsApi() { - return Auth.CredentialsApi; - } - - public FirebaseUser getCurrentUser() { - return getFirebaseAuth().getCurrentUser(); - } - - public static Intent createBaseIntent( - @NonNull Context context, - @NonNull Class target, - @NonNull FlowParameters flowParams) { - return new Intent( - checkNotNull(context, "context cannot be null"), - checkNotNull(target, "target activity cannot be null")) - .putExtra(ExtraConstants.EXTRA_FLOW_PARAMS, - checkNotNull(flowParams, "flowParams cannot be null")); - } - - public SmartLock getSmartLockInstance(AppCompatBase activity, String tag) { - return SmartLock.getInstance(activity, tag); + finishActivity(mActivity, resultCode, intent); } - public void saveCredentialsOrFinish( - @Nullable SmartLock smartLock, - AppCompatBase activity, - FirebaseUser firebaseUser, - @NonNull IdpResponse response) { - saveCredentialsOrFinish(smartLock, activity, firebaseUser, null, response); + public SaveSmartLock getSaveSmartLockInstance() { + return getSaveSmartLockInstance(mActivity); } public void saveCredentialsOrFinish( - @Nullable SmartLock smartLock, - AppCompatBase activity, + @Nullable SaveSmartLock saveSmartLock, FirebaseUser firebaseUser, @NonNull String password) { - saveCredentialsOrFinish(smartLock, activity, firebaseUser, password, null); - } - - private void saveCredentialsOrFinish( - @Nullable SmartLock smartLock, - AppCompatBase activity, - FirebaseUser firebaseUser, - @Nullable String password, - @Nullable IdpResponse response) { - if (smartLock == null) { - activity.finish(RESULT_OK, new Intent()); - } else { - smartLock.saveCredentialsOrFinish( - activity, - this, - firebaseUser, - password, - response); - } + saveCredentialsOrFinish(saveSmartLock, mActivity, firebaseUser, password, null); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/BaseDialog.java b/auth/src/main/java/com/firebase/ui/auth/ui/BaseDialog.java new file mode 100644 index 000000000..a28aedd20 --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/ui/BaseDialog.java @@ -0,0 +1,26 @@ +package com.firebase.ui.auth.ui; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; + +public class BaseDialog extends DialogFragment { + protected FragmentHelper mHelper; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mHelper = new FragmentHelper(this); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mHelper.dismissDialog(); + } + + public void finish(int resultCode, Intent resultIntent) { + mHelper.finish(resultCode, resultIntent); + } +} diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/BaseFragment.java b/auth/src/main/java/com/firebase/ui/auth/ui/BaseFragment.java new file mode 100644 index 000000000..eb3bd4541 --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/ui/BaseFragment.java @@ -0,0 +1,26 @@ +package com.firebase.ui.auth.ui; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; + +public class BaseFragment extends Fragment { + protected FragmentHelper mHelper; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mHelper = new FragmentHelper(this); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mHelper.dismissDialog(); + } + + public void finish(int resultCode, Intent resultIntent) { + mHelper.finish(resultCode, resultIntent); + } +} diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/BaseHelper.java b/auth/src/main/java/com/firebase/ui/auth/ui/BaseHelper.java new file mode 100644 index 000000000..c2d53be7a --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/ui/BaseHelper.java @@ -0,0 +1,128 @@ +package com.firebase.ui.auth.ui; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; +import android.support.v4.app.FragmentActivity; + +import com.firebase.ui.auth.IdpResponse; +import com.firebase.ui.auth.util.signincontainer.SaveSmartLock; +import com.google.android.gms.auth.api.Auth; +import com.google.android.gms.auth.api.credentials.CredentialsApi; +import com.google.firebase.FirebaseApp; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; + +import static android.app.Activity.RESULT_OK; +import static com.firebase.ui.auth.util.Preconditions.checkNotNull; + +public class BaseHelper { + protected Context mContext; + private final FlowParameters mFlowParams; + private ProgressDialog mProgressDialog; + + public BaseHelper(Context context, FlowParameters parameters) { + mContext = context; + mFlowParams = parameters; + } + + public void configureTheme() { + mContext.setTheme(mFlowParams.themeId); + } + + public FlowParameters getFlowParams() { + return mFlowParams; + } + + public void finishActivity(Activity activity, int resultCode, Intent intent) { + activity.setResult(resultCode, intent); + activity.finish(); + } + + public void showLoadingDialog(String message) { + dismissDialog(); + mProgressDialog = ProgressDialog.show(mContext, "", message, true); + } + + public void showLoadingDialog(@StringRes int stringResource) { + showLoadingDialog(mContext.getString(stringResource)); + } + + public void dismissDialog() { + if (mProgressDialog != null) { + mProgressDialog.dismiss(); + mProgressDialog = null; + } + } + + public boolean isProgressDialogShowing() { + return mProgressDialog != null && mProgressDialog.isShowing(); + } + + public Context getApplicationContext() { + return mContext.getApplicationContext(); + } + + public String getAppName() { + return mFlowParams.appName; + } + + public FirebaseApp getFirebaseApp() { + return FirebaseApp.getInstance(mFlowParams.appName); + } + + public FirebaseAuth getFirebaseAuth() { + return FirebaseAuth.getInstance(getFirebaseApp()); + } + + public CredentialsApi getCredentialsApi() { + return Auth.CredentialsApi; + } + + public FirebaseUser getCurrentUser() { + return getFirebaseAuth().getCurrentUser(); + } + + public static Intent createBaseIntent( + @NonNull Context context, + @NonNull Class target, + @NonNull FlowParameters flowParams) { + return new Intent( + checkNotNull(context, "context cannot be null"), + checkNotNull(target, "target activity cannot be null")) + .putExtra(ExtraConstants.EXTRA_FLOW_PARAMS, + checkNotNull(flowParams, "flowParams cannot be null")); + } + + public SaveSmartLock getSaveSmartLockInstance(FragmentActivity activity) { + return SaveSmartLock.getInstance(activity, getFlowParams()); + } + + public void saveCredentialsOrFinish( + @Nullable SaveSmartLock saveSmartLock, + Activity activity, + FirebaseUser firebaseUser, + @NonNull IdpResponse response) { + saveCredentialsOrFinish(saveSmartLock, activity, firebaseUser, null, response); + } + + public void saveCredentialsOrFinish( + @Nullable SaveSmartLock saveSmartLock, + Activity activity, + FirebaseUser firebaseUser, + @Nullable String password, + @Nullable IdpResponse response) { + if (saveSmartLock == null) { + finishActivity(activity, RESULT_OK, new Intent()); + } else { + saveSmartLock.saveCredentialsOrFinish( + firebaseUser, + password, + response); + } + } +} diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/ChooseAccountActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/ChooseAccountActivity.java deleted file mode 100644 index 73af3dcbf..000000000 --- a/auth/src/main/java/com/firebase/ui/auth/ui/ChooseAccountActivity.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.firebase.ui.auth.ui; - -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.ConnectivityManager; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.text.TextUtils; -import android.util.Log; - -import com.firebase.ui.auth.AuthUI.IdpConfig; -import com.firebase.ui.auth.BuildConfig; -import com.firebase.ui.auth.ui.idp.AuthMethodPickerActivity; -import com.firebase.ui.auth.ui.idp.IdpSignInContainerActivity; -import com.firebase.ui.auth.util.CredentialsAPI; -import com.firebase.ui.auth.util.CredentialsApiHelper; -import com.firebase.ui.auth.util.EmailFlowUtil; -import com.firebase.ui.auth.util.PlayServicesHelper; -import com.google.android.gms.auth.api.credentials.Credential; -import com.google.android.gms.auth.api.credentials.CredentialsApi; -import com.google.android.gms.auth.api.credentials.IdentityProviders; -import com.google.android.gms.common.api.Status; -import com.google.android.gms.tasks.OnCompleteListener; -import com.google.android.gms.tasks.OnFailureListener; -import com.google.android.gms.tasks.OnSuccessListener; -import com.google.android.gms.tasks.Task; -import com.google.firebase.auth.AuthResult; -import com.google.firebase.auth.EmailAuthProvider; -import com.google.firebase.auth.FacebookAuthProvider; -import com.google.firebase.auth.FirebaseAuthInvalidUserException; -import com.google.firebase.auth.GoogleAuthProvider; -import com.google.firebase.auth.TwitterAuthProvider; - -import java.util.List; - -import static com.firebase.ui.auth.ui.ResultCodes.RESULT_NO_NETWORK; - -/** - * Attempts to acquire a credential from Smart Lock for Passwords to sign in - * an existing account. If this succeeds, an attempt is made to sign the user in - * with this credential. If it does not, the - * {@link AuthMethodPickerActivity authentication method picker activity} - * is started, unless only email is supported, in which case the - * {@link com.firebase.ui.auth.ui.email.SignInNoPasswordActivity email sign-in flow} - * is started. - */ -public class ChooseAccountActivity extends ActivityBase { - private static final String TAG = "ChooseAccountActivity"; - - private static final int RC_CREDENTIALS_READ = 2; - private static final int RC_IDP_SIGNIN = 3; - private static final int RC_AUTH_METHOD_PICKER = 4; - private static final int RC_EMAIL_FLOW = 5; - private static final int RC_PLAY_SERVICES = 6; - - private CredentialsAPI mCredentialsApi; - private PlayServicesHelper mPlayServicesHelper; - - @Override - protected void onCreate(Bundle savedInstance) { - super.onCreate(savedInstance); - - if (!hasNetworkConnection()) { - Log.d(TAG, "No network connection"); - finish(RESULT_NO_NETWORK, new Intent()); - return; - } - - // Make Google Play Services available at the correct version, if possible - mPlayServicesHelper = PlayServicesHelper.getInstance(this); - boolean madeAvailable = mPlayServicesHelper - .makePlayServicesAvailable(this, RC_PLAY_SERVICES, - new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialogInterface) { - Log.w(TAG, "playServices:dialog.onCancel()"); - finish(RESULT_CANCELED, new Intent()); - } - }); - - if (!madeAvailable) { - Log.w(TAG, "playServices: could not make available."); - finish(RESULT_CANCELED, new Intent()); - return; - } - - mCredentialsApi = new CredentialsAPI(this, new CredentialsAPI.CallbackInterface() { - @Override - public void onAsyncTaskFinished() { - onCredentialsApiConnected(mCredentialsApi, mActivityHelper); - } - }); - } - - @Override - protected void onStart() { - super.onStart(); - if (mCredentialsApi != null) { - mCredentialsApi.onStart(); - } - } - - @Override - protected void onStop() { - super.onStop(); - if (mCredentialsApi != null) { - mCredentialsApi.onStop(); - } - } - - /** - * Check if there is an active or soon-to-be-active network connection. - */ - private boolean hasNetworkConnection() { - ConnectivityManager manager = - (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - - return manager != null - && manager.getActiveNetworkInfo() != null - && manager.getActiveNetworkInfo().isConnectedOrConnecting(); - } - - /** - * Called when the Credentials API connects. - */ - public void onCredentialsApiConnected( - CredentialsAPI credentialsApi, - ActivityHelper activityHelper) { - String email = credentialsApi.getEmailFromCredential(); - String password = credentialsApi.getPasswordFromCredential(); - String accountType = credentialsApi.getAccountTypeFromCredential(); - - FlowParameters flowParams = activityHelper.getFlowParams(); - - if (flowParams.smartLockEnabled - && mPlayServicesHelper.isPlayServicesAvailable() - && credentialsApi.isCredentialsAvailable()) { - - // Attempt auto-sign in using SmartLock - if (credentialsApi.isAutoSignInAvailable()) { - credentialsApi.googleSilentSignIn(); - if (!TextUtils.isEmpty(password)) { - // Sign in with the email/password retrieved from SmartLock - signInWithEmailAndPassword(activityHelper, email, password); - } else { - // log in with id/provider - redirectToIdpSignIn(email, accountType); - } - } else if (credentialsApi.isSignInResolutionNeeded()) { - // resolve credential - credentialsApi.resolveSavedEmails(this); - } else { - startAuthMethodChoice(activityHelper); - } - } else { - startAuthMethodChoice(activityHelper); - } - } - - private void startAuthMethodChoice(ActivityHelper activityHelper) { - List idpConfigs = activityHelper.getFlowParams().providerInfo; - - // If the only provider is Email, immediately launch the email flow. Otherwise, launch - // the auth method picker screen. - if (idpConfigs.size() == 1 - && idpConfigs.get(0).getProviderId().equals(EmailAuthProvider.PROVIDER_ID)) { - startActivityForResult( - EmailFlowUtil.createIntent( - this, - activityHelper.getFlowParams()), - RC_EMAIL_FLOW); - } else { - startActivityForResult( - AuthMethodPickerActivity.createIntent( - this, - activityHelper.getFlowParams()), - RC_AUTH_METHOD_PICKER); - } - } - - private void logInWithCredential( - final String email, - final String password, - final String accountType) { - - if (email != null - && mCredentialsApi.isCredentialsAvailable() - && !mCredentialsApi.isSignInResolutionNeeded()) { - if (password != null && !password.isEmpty()) { - // email/password combination - signInWithEmailAndPassword(mActivityHelper, email, password); - } else { - // identifier/provider combination - redirectToIdpSignIn(email, accountType); - } - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (BuildConfig.DEBUG) { - Log.d(TAG, "onActivityResult:" + requestCode + ":" + resultCode + ":" + data); - } - - switch (requestCode) { - case RC_CREDENTIALS_READ: - if (resultCode == RESULT_OK) { - // credential selected from SmartLock, log in with that credential - Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY); - mCredentialsApi.handleCredential(credential); - mCredentialsApi.resolveSignIn(); - logInWithCredential( - mCredentialsApi.getEmailFromCredential(), - mCredentialsApi.getPasswordFromCredential(), - mCredentialsApi.getAccountTypeFromCredential() - ); - } else if (resultCode == RESULT_CANCELED - || resultCode == CredentialsApi.ACTIVITY_RESULT_OTHER_ACCOUNT) { - // Smart lock selector cancelled, go to the AuthMethodPicker screen - startAuthMethodChoice(mActivityHelper); - } else if (resultCode == RESULT_FIRST_USER) { - // TODO: (serikb) figure out flow - } - break; - case RC_IDP_SIGNIN: - case RC_AUTH_METHOD_PICKER: - case RC_EMAIL_FLOW: - finish(resultCode, data); - break; - case RC_PLAY_SERVICES: - if (resultCode != RESULT_OK) { - finish(resultCode, new Intent()); - } - break; - } - } - - /** - * Begin sign in process with email and password from a SmartLock credential. - * On success, finish with {@link #RESULT_OK}. - * On failure, delete the credential from SmartLock (if applicable) and then launch the - * auth method picker flow. - */ - private void signInWithEmailAndPassword(ActivityHelper helper, String email, String password) { - helper.getFirebaseAuth() - .signInWithEmailAndPassword(email, password) - .addOnFailureListener(new TaskFailureLogger( - TAG, "Error signing in with email and password")) - .addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(AuthResult authResult) { - finish(RESULT_OK, new Intent()); - } - }) - .addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { - if (e instanceof FirebaseAuthInvalidUserException) { - // In this case the credential saved in SmartLock was not - // a valid credential, we should delete it from SmartLock - // before continuing. - deleteCredentialAndRedirect(); - } else { - startAuthMethodChoice(mActivityHelper); - } - } - }); - } - - /** - * Delete the last credential retrieved from SmartLock and then redirect to the - * auth method choice flow. - */ - private void deleteCredentialAndRedirect() { - if (mCredentialsApi.getCredential() == null) { - Log.w(TAG, "deleteCredentialAndRedirect: null credential"); - startAuthMethodChoice(mActivityHelper); - return; - } - - CredentialsApiHelper credentialsApiHelper = CredentialsApiHelper.getInstance(this); - credentialsApiHelper.delete(mCredentialsApi.getCredential()) - .addOnCompleteListener(this, new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (!task.isSuccessful()) { - Log.w(TAG, "deleteCredential:failure", task.getException()); - } - startAuthMethodChoice(mActivityHelper); - } - }); - } - - protected void redirectToIdpSignIn(String email, String accountType) { - Intent nextIntent; - switch (accountType) { - case IdentityProviders.GOOGLE: - nextIntent = IdpSignInContainerActivity.createIntent( - this, - mActivityHelper.getFlowParams(), - GoogleAuthProvider.PROVIDER_ID, - email); - break; - case IdentityProviders.FACEBOOK: - nextIntent = IdpSignInContainerActivity.createIntent( - this, - mActivityHelper.getFlowParams(), - FacebookAuthProvider.PROVIDER_ID, - email); - break; - case IdentityProviders.TWITTER: - nextIntent = IdpSignInContainerActivity.createIntent( - this, - mActivityHelper.getFlowParams(), - TwitterAuthProvider.PROVIDER_ID, - email); - break; - default: - Log.w(TAG, "unknown provider: " + accountType); - nextIntent = AuthMethodPickerActivity.createIntent( - this, - mActivityHelper.getFlowParams()); - } - this.startActivityForResult(nextIntent, RC_IDP_SIGNIN); - } - - public static Intent createIntent( - Context context, - FlowParameters flowParams) { - return ActivityHelper.createBaseIntent(context, ChooseAccountActivity.class, flowParams); - } -} diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/ExtraConstants.java b/auth/src/main/java/com/firebase/ui/auth/ui/ExtraConstants.java index d79df3a51..6d144eed1 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/ExtraConstants.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/ExtraConstants.java @@ -23,4 +23,5 @@ public class ExtraConstants { public static final String EXTRA_FLOW_PARAMS = "extra_flow_params"; public static final String EXTRA_IDP_RESPONSE = "extra_idp_response"; public static final String EXTRA_PROVIDER = "extra_provider"; + public static final String HAS_EXISTING_INSTANCE = "has_existing_instance"; } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/FragmentHelper.java b/auth/src/main/java/com/firebase/ui/auth/ui/FragmentHelper.java new file mode 100644 index 000000000..6868b38af --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/ui/FragmentHelper.java @@ -0,0 +1,31 @@ +package com.firebase.ui.auth.ui; + +import android.content.Intent; +import android.content.IntentSender; +import android.os.Bundle; +import android.support.v4.app.Fragment; + +public class FragmentHelper extends BaseHelper { + private Fragment mFragment; + + public FragmentHelper(Fragment fragment) { + super(fragment.getContext(), (FlowParameters) fragment.getArguments() + .getParcelable(ExtraConstants.EXTRA_FLOW_PARAMS)); + mFragment = fragment; + } + + public void finish(int resultCode, Intent intent) { + finishActivity(mFragment.getActivity(), resultCode, intent); + } + + public static Bundle getFlowParamsBundle(FlowParameters params) { + Bundle bundle = new Bundle(); + bundle.putParcelable(ExtraConstants.EXTRA_FLOW_PARAMS, params); + return bundle; + } + + public void startIntentSenderForResult(IntentSender sender, int requestCode) + throws IntentSender.SendIntentException { + mFragment.startIntentSenderForResult(sender, requestCode, null, 0, 0, 0, null); + } +} diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/account_link/WelcomeBackIdpPrompt.java b/auth/src/main/java/com/firebase/ui/auth/ui/account_link/WelcomeBackIdpPrompt.java index 99c8205a7..1ee5564eb 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/account_link/WelcomeBackIdpPrompt.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/account_link/WelcomeBackIdpPrompt.java @@ -28,14 +28,14 @@ import com.firebase.ui.auth.AuthUI.IdpConfig; import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.R; +import com.firebase.ui.auth.provider.AuthCredentialHelper; import com.firebase.ui.auth.provider.FacebookProvider; import com.firebase.ui.auth.provider.GoogleProvider; import com.firebase.ui.auth.provider.IdpProvider; import com.firebase.ui.auth.provider.IdpProvider.IdpCallback; import com.firebase.ui.auth.provider.TwitterProvider; -import com.firebase.ui.auth.ui.ActivityHelper; import com.firebase.ui.auth.ui.AppCompatBase; -import com.firebase.ui.auth.ui.AuthCredentialHelper; +import com.firebase.ui.auth.ui.BaseHelper; import com.firebase.ui.auth.ui.ExtraConstants; import com.firebase.ui.auth.ui.FlowParameters; import com.firebase.ui.auth.ui.TaskFailureLogger; @@ -176,7 +176,6 @@ public void onComplete(@NonNull Task task) { TAG, "Error signing in with previous credential")) .addOnCompleteListener(new FinishListener(newIdpResponse)); } else { - mActivityHelper.dismissDialog(); finish(Activity.RESULT_OK, new Intent().putExtra( ExtraConstants.EXTRA_IDP_RESPONSE, newIdpResponse)); } @@ -198,7 +197,7 @@ public static Intent createIntent( String providerId, IdpResponse idpResponse, String email) { - return ActivityHelper.createBaseIntent(context, WelcomeBackIdpPrompt.class, flowParams) + return BaseHelper.createBaseIntent(context, WelcomeBackIdpPrompt.class, flowParams) .putExtra(ExtraConstants.EXTRA_PROVIDER, providerId) .putExtra(ExtraConstants.EXTRA_IDP_RESPONSE, idpResponse) .putExtra(ExtraConstants.EXTRA_EMAIL, email); @@ -212,7 +211,6 @@ private class FinishListener implements OnCompleteListener { } public void onComplete(@NonNull Task task) { - mActivityHelper.dismissDialog(); finish(Activity.RESULT_OK, new Intent().putExtra(ExtraConstants.EXTRA_IDP_RESPONSE, mIdpResponse)); } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/account_link/WelcomeBackPasswordPrompt.java b/auth/src/main/java/com/firebase/ui/auth/ui/account_link/WelcomeBackPasswordPrompt.java index f4bce28be..655b420c8 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/account_link/WelcomeBackPasswordPrompt.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/account_link/WelcomeBackPasswordPrompt.java @@ -31,15 +31,15 @@ import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.R; -import com.firebase.ui.auth.ui.ActivityHelper; +import com.firebase.ui.auth.provider.AuthCredentialHelper; import com.firebase.ui.auth.ui.AppCompatBase; -import com.firebase.ui.auth.ui.AuthCredentialHelper; +import com.firebase.ui.auth.ui.BaseHelper; import com.firebase.ui.auth.ui.ExtraConstants; import com.firebase.ui.auth.ui.FlowParameters; import com.firebase.ui.auth.ui.TaskFailureLogger; import com.firebase.ui.auth.ui.email.PasswordToggler; import com.firebase.ui.auth.ui.email.RecoverPasswordActivity; -import com.firebase.ui.auth.util.SmartLock; +import com.firebase.ui.auth.util.signincontainer.SaveSmartLock; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.firebase.auth.AuthCredential; @@ -58,14 +58,13 @@ public class WelcomeBackPasswordPrompt extends AppCompatBase implements View.OnC private TextInputLayout mPasswordLayout; private EditText mPasswordField; private IdpResponse mIdpResponse; - @Nullable - private SmartLock mSmartLock; + @Nullable private SaveSmartLock mSaveSmartLock; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.welcome_back_password_prompt_layout); - mSmartLock = SmartLock.getInstance(WelcomeBackPasswordPrompt.this, TAG); + mSaveSmartLock = mActivityHelper.getSaveSmartLockInstance(); mPasswordLayout = (TextInputLayout) findViewById(R.id.password_layout); mPasswordField = (EditText) findViewById(R.id.password); @@ -83,8 +82,6 @@ protected void onCreate(Bundle savedInstanceState) { TextView bodyTextView = ((TextView) findViewById(R.id.welcome_back_password_body)); bodyTextView.setText(spannableStringBuilder); - ((TextInputLayout) findViewById(R.id.password_layout)).setPasswordVisibilityToggleEnabled(false); - // Click listeners findViewById(R.id.button_done).setOnClickListener(this); findViewById(R.id.toggle_visibility).setOnClickListener(new PasswordToggler(mPasswordField)); @@ -98,7 +95,6 @@ public void onClick(View view) { mActivityHelper.showLoadingDialog(R.string.progress_dialog_signing_in); next(mEmail, mPasswordField.getText().toString()); } else if (id == R.id.trouble_signing_in) { - mActivityHelper.dismissDialog(); startActivity(RecoverPasswordActivity.createIntent( getApplicationContext(), mActivityHelper.getFlowParams(), @@ -141,8 +137,7 @@ public void onSuccess(AuthResult authResult) { @Override public void onSuccess(AuthResult authResult) { mActivityHelper.saveCredentialsOrFinish( - mSmartLock, - WelcomeBackPasswordPrompt.this, + mSaveSmartLock, authResult.getUser(), password); } @@ -163,7 +158,7 @@ public static Intent createIntent( Context context, FlowParameters flowParams, IdpResponse response) { - return ActivityHelper.createBaseIntent(context, WelcomeBackPasswordPrompt.class, flowParams) + return BaseHelper.createBaseIntent(context, WelcomeBackPasswordPrompt.class, flowParams) .putExtra(ExtraConstants.EXTRA_IDP_RESPONSE, response); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/AcquireEmailHelper.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/AcquireEmailHelper.java index ae0bec73d..a46bf8aa9 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/AcquireEmailHelper.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/AcquireEmailHelper.java @@ -16,6 +16,7 @@ import android.content.Intent; import android.support.annotation.NonNull; +import android.text.TextUtils; import com.firebase.ui.auth.R; import com.firebase.ui.auth.ui.ActivityHelper; @@ -50,7 +51,7 @@ public AcquireEmailHelper(ActivityHelper activityHelper) { public void checkAccountExists(final String email) { FirebaseAuth firebaseAuth = mActivityHelper.getFirebaseAuth(); mActivityHelper.showLoadingDialog(R.string.progress_dialog_loading); - if (email != null && !email.isEmpty()) { + if (!TextUtils.isEmpty(email)) { firebaseAuth .fetchProvidersForEmail(email) .addOnFailureListener( diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/ConfirmRecoverPasswordActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/ConfirmRecoverPasswordActivity.java deleted file mode 100644 index bf6923705..000000000 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/ConfirmRecoverPasswordActivity.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.firebase.ui.auth.ui.email; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.widget.TextView; - -import com.firebase.ui.auth.R; -import com.firebase.ui.auth.ui.ActivityHelper; -import com.firebase.ui.auth.ui.ExtraConstants; -import com.firebase.ui.auth.ui.FlowParameters; - -/** - * Dialog activity to confirm successful email sending in {@link RecoverPasswordActivity}. - */ -public class ConfirmRecoverPasswordActivity extends AppCompatActivity - implements View.OnClickListener { - - private ActivityHelper mActivityHelper; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mActivityHelper = new ActivityHelper(this, getIntent()); - // intentionally do not configure the theme on this activity, it is a dialog - - setContentView(R.layout.confirm_recovery_layout); - String email = getIntent().getStringExtra(ExtraConstants.EXTRA_EMAIL); - - String text = String.format( - getResources().getString(R.string.confirm_recovery_body), - email); - ((TextView) findViewById(R.id.body_text)).setText(text); - - findViewById(R.id.button_done).setOnClickListener(this); - } - - @Override - public void onClick(View view) { - if (view.getId() == R.id.button_done) { - finish(RESULT_OK, new Intent()); - } - } - - public static Intent createIntent( - Context context, - FlowParameters flowParams, - String email) { - return ActivityHelper.createBaseIntent(context, - ConfirmRecoverPasswordActivity.class, - flowParams) - .putExtra(ExtraConstants.EXTRA_EMAIL, email); - } - - private void finish(int resultCode, Intent intent) { - mActivityHelper.finish(resultCode, intent); - } -} diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailHintContainerActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailHintContainerActivity.java index ce31a5462..4ce1ea1bd 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailHintContainerActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailHintContainerActivity.java @@ -19,11 +19,10 @@ import android.content.Intent; import android.content.IntentSender; import android.os.Bundle; - import android.util.Log; -import com.firebase.ui.auth.ui.ActivityHelper; import com.firebase.ui.auth.ui.AppCompatBase; +import com.firebase.ui.auth.ui.BaseHelper; import com.firebase.ui.auth.ui.FlowParameters; import com.firebase.ui.auth.util.FirebaseAuthWrapper; import com.firebase.ui.auth.util.FirebaseAuthWrapperFactory; @@ -77,7 +76,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { public static Intent createIntent( Context context, FlowParameters flowParams) { - return ActivityHelper.createBaseIntent( + return BaseHelper.createBaseIntent( context, EmailHintContainerActivity.class, flowParams); diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/RecoverPasswordActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/RecoverPasswordActivity.java index 5979d0a0d..f7e04cf75 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/RecoverPasswordActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/RecoverPasswordActivity.java @@ -23,8 +23,8 @@ import android.widget.EditText; import com.firebase.ui.auth.R; -import com.firebase.ui.auth.ui.ActivityHelper; import com.firebase.ui.auth.ui.AppCompatBase; +import com.firebase.ui.auth.ui.BaseHelper; import com.firebase.ui.auth.ui.ExtraConstants; import com.firebase.ui.auth.ui.FlowParameters; import com.firebase.ui.auth.ui.TaskFailureLogger; @@ -39,7 +39,6 @@ */ public class RecoverPasswordActivity extends AppCompatBase implements View.OnClickListener { private static final String TAG = "RecoverPasswordActivity"; - private static final int RC_CONFIRM = 3; private EditText mEmailEditText; private EmailFieldValidator mEmailFieldValidator; @@ -70,12 +69,7 @@ private void next(final String email) { @Override public void onSuccess(Void aVoid) { mActivityHelper.dismissDialog(); - - Intent confirmIntent = ConfirmRecoverPasswordActivity.createIntent( - RecoverPasswordActivity.this, - mActivityHelper.getFlowParams(), - email); - startActivityForResult(confirmIntent, RC_CONFIRM); + RecoveryEmailSentDialog.show(email, getSupportFragmentManager()); } }) .addOnFailureListener(this, new OnFailureListener() { @@ -91,12 +85,6 @@ public void onFailure(@NonNull Exception e) { }); } - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == RC_CONFIRM) { - finish(RESULT_OK, new Intent()); - } - } - @Override public void onClick(View view) { @@ -110,7 +98,7 @@ public void onClick(View view) { } public static Intent createIntent(Context context, FlowParameters flowParams, String email) { - return ActivityHelper.createBaseIntent(context, RecoverPasswordActivity.class, flowParams) + return BaseHelper.createBaseIntent(context, RecoverPasswordActivity.class, flowParams) .putExtra(ExtraConstants.EXTRA_EMAIL, email); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/RecoveryEmailSentDialog.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/RecoveryEmailSentDialog.java new file mode 100644 index 000000000..8ea407aee --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/RecoveryEmailSentDialog.java @@ -0,0 +1,43 @@ +package com.firebase.ui.auth.ui.email; + +import android.app.Activity; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.FragmentManager; +import android.support.v7.app.AlertDialog; + +import com.firebase.ui.auth.R; +import com.firebase.ui.auth.ui.BaseDialog; +import com.firebase.ui.auth.ui.ExtraConstants; + +public class RecoveryEmailSentDialog extends BaseDialog { + private static final String TAG = "RecoveryEmailSentDialog"; + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getContext(), R.style.FirebaseUI_Dialog) + .setTitle(R.string.title_confirm_recover_password_activity) + .setMessage(String.format(getString(R.string.confirm_recovery_body), + getArguments().getString(ExtraConstants.EXTRA_EMAIL))) + .setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface anInterface) { + finish(Activity.RESULT_OK, new Intent()); + } + }) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + + public static void show(String email, FragmentManager manager) { + RecoveryEmailSentDialog result = new RecoveryEmailSentDialog(); + Bundle bundle = new Bundle(); + bundle.putString(ExtraConstants.EXTRA_EMAIL, email); + result.setArguments(bundle); + result.show(manager, TAG); + } +} diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailActivity.java index 39231268b..84f85b781 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailActivity.java @@ -32,15 +32,15 @@ import android.widget.TextView; import com.firebase.ui.auth.R; -import com.firebase.ui.auth.ui.ActivityHelper; import com.firebase.ui.auth.ui.AppCompatBase; +import com.firebase.ui.auth.ui.BaseHelper; import com.firebase.ui.auth.ui.ExtraConstants; import com.firebase.ui.auth.ui.FlowParameters; import com.firebase.ui.auth.ui.TaskFailureLogger; import com.firebase.ui.auth.ui.email.field_validators.EmailFieldValidator; import com.firebase.ui.auth.ui.email.field_validators.PasswordFieldValidator; import com.firebase.ui.auth.ui.email.field_validators.RequiredFieldValidator; -import com.firebase.ui.auth.util.SmartLock; +import com.firebase.ui.auth.util.signincontainer.SaveSmartLock; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; @@ -66,14 +66,14 @@ public class RegisterEmailActivity extends AppCompatBase implements View.OnClick private PasswordFieldValidator mPasswordFieldValidator; private RequiredFieldValidator mNameValidator; @Nullable - private SmartLock mSmartLock; + private SaveSmartLock mSaveSmartLock; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.register_email_layout); - mSmartLock = mActivityHelper.getSmartLockInstance(this, TAG); + mSaveSmartLock = mActivityHelper.getSaveSmartLockInstance(); String email = getIntent().getStringExtra(ExtraConstants.EXTRA_EMAIL); mEmailEditText = (EditText) findViewById(R.id.email); @@ -85,7 +85,6 @@ protected void onCreate(Bundle savedInstanceState) { getResources().getValue(R.dimen.slightly_visible_icon, slightlyVisibleIcon, true); mPasswordEditText = (EditText) findViewById(R.id.password); - ((TextInputLayout) findViewById(R.id.password_layout)).setPasswordVisibilityToggleEnabled(false); ImageView togglePasswordImage = (ImageView) findViewById(R.id.toggle_visibility); mPasswordEditText.setOnFocusChangeListener(new ImageFocusTransparencyChanger( @@ -161,8 +160,7 @@ public void onComplete(@NonNull Task task) { // the account creation succeeded and we want to save // the credential to SmartLock (if enabled). mActivityHelper.saveCredentialsOrFinish( - mSmartLock, - RegisterEmailActivity.this, + mSaveSmartLock, firebaseUser, password); } @@ -218,7 +216,7 @@ public static Intent createIntent( Context context, FlowParameters flowParams, String email) { - return ActivityHelper.createBaseIntent(context, RegisterEmailActivity.class, flowParams) + return BaseHelper.createBaseIntent(context, RegisterEmailActivity.class, flowParams) .putExtra(ExtraConstants.EXTRA_EMAIL, email); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/SignInActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/SignInActivity.java index 2e07ddfe5..864ece2f8 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/SignInActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/SignInActivity.java @@ -28,14 +28,14 @@ import android.widget.TextView; import com.firebase.ui.auth.R; -import com.firebase.ui.auth.ui.ActivityHelper; import com.firebase.ui.auth.ui.AppCompatBase; +import com.firebase.ui.auth.ui.BaseHelper; import com.firebase.ui.auth.ui.ExtraConstants; import com.firebase.ui.auth.ui.FlowParameters; import com.firebase.ui.auth.ui.TaskFailureLogger; import com.firebase.ui.auth.ui.email.field_validators.EmailFieldValidator; import com.firebase.ui.auth.ui.email.field_validators.RequiredFieldValidator; -import com.firebase.ui.auth.util.SmartLock; +import com.firebase.ui.auth.util.signincontainer.SaveSmartLock; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.firebase.auth.AuthResult; @@ -51,14 +51,14 @@ public class SignInActivity extends AppCompatBase implements View.OnClickListene private EmailFieldValidator mEmailValidator; private RequiredFieldValidator mPasswordValidator; @Nullable - private SmartLock mSmartLock; + private SaveSmartLock mSaveSmartLock; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sign_in_layout); - mSmartLock = mActivityHelper.getSmartLockInstance(this, TAG); + mSaveSmartLock = mActivityHelper.getSaveSmartLockInstance(); String email = getIntent().getStringExtra(ExtraConstants.EXTRA_EMAIL); @@ -71,7 +71,6 @@ protected void onCreate(Bundle savedInstanceState) { getResources().getValue(R.dimen.slightly_visible_icon, slightlyVisibleIcon, true); mPasswordEditText = (EditText) findViewById(R.id.password); - ((TextInputLayout) findViewById(R.id.password_layout)).setPasswordVisibilityToggleEnabled(false); ImageView togglePasswordImage = (ImageView) findViewById(R.id.toggle_visibility); mPasswordEditText.setOnFocusChangeListener(new ImageFocusTransparencyChanger( @@ -103,8 +102,7 @@ private void signIn(final String email, final String password) { public void onSuccess(AuthResult authResult) { // Save credential in SmartLock (if enabled) mActivityHelper.saveCredentialsOrFinish( - mSmartLock, - SignInActivity.this, + mSaveSmartLock, authResult.getUser(), password); } @@ -118,7 +116,6 @@ public void onFailure(@NonNull Exception e) { TextInputLayout passwordInput = (TextInputLayout) findViewById(R.id.password_layout); passwordInput.setError(getString(R.string.login_error)); - mActivityHelper.dismissDialog(); } }); } @@ -144,7 +141,7 @@ public static Intent createIntent( Context context, FlowParameters flowParams, String email) { - return ActivityHelper.createBaseIntent(context, SignInActivity.class, flowParams) + return BaseHelper.createBaseIntent(context, SignInActivity.class, flowParams) .putExtra(ExtraConstants.EXTRA_EMAIL, email); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/SignInNoPasswordActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/SignInNoPasswordActivity.java index 3be643054..66f83c208 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/SignInNoPasswordActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/SignInNoPasswordActivity.java @@ -24,8 +24,8 @@ import android.widget.EditText; import com.firebase.ui.auth.R; -import com.firebase.ui.auth.ui.ActivityHelper; import com.firebase.ui.auth.ui.AppCompatBase; +import com.firebase.ui.auth.ui.BaseHelper; import com.firebase.ui.auth.ui.ExtraConstants; import com.firebase.ui.auth.ui.FlowParameters; import com.firebase.ui.auth.ui.email.field_validators.EmailFieldValidator; @@ -76,7 +76,7 @@ public static Intent createIntent( Context context, FlowParameters flowParams, String email) { - return ActivityHelper.createBaseIntent(context, SignInNoPasswordActivity.class, flowParams) + return BaseHelper.createBaseIntent(context, SignInNoPasswordActivity.class, flowParams) .putExtra(ExtraConstants.EXTRA_EMAIL, email); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java index 2de463aaa..11ca44ce3 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java @@ -28,19 +28,19 @@ import com.firebase.ui.auth.BuildConfig; import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.R; +import com.firebase.ui.auth.provider.AuthCredentialHelper; import com.firebase.ui.auth.provider.FacebookProvider; import com.firebase.ui.auth.provider.GoogleProvider; import com.firebase.ui.auth.provider.IdpProvider; import com.firebase.ui.auth.provider.IdpProvider.IdpCallback; import com.firebase.ui.auth.provider.TwitterProvider; -import com.firebase.ui.auth.ui.ActivityHelper; import com.firebase.ui.auth.ui.AppCompatBase; -import com.firebase.ui.auth.ui.AuthCredentialHelper; +import com.firebase.ui.auth.ui.BaseHelper; import com.firebase.ui.auth.ui.FlowParameters; import com.firebase.ui.auth.ui.TaskFailureLogger; import com.firebase.ui.auth.ui.email.EmailHintContainerActivity; import com.firebase.ui.auth.util.EmailFlowUtil; -import com.firebase.ui.auth.util.SmartLock; +import com.firebase.ui.auth.util.signincontainer.SaveSmartLock; import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.FacebookAuthProvider; import com.google.firebase.auth.FirebaseAuth; @@ -52,7 +52,7 @@ /** * Presents the list of authentication options for this app to the user. If an - * identity provider option is selected, a {@link IdpSignInContainerActivity container activity} + * identity provider option is selected, a {@link CredentialSignInHandler} * is launched to manage the IDP-specific sign-in flow. If email authentication is chosen, * the {@link EmailHintContainerActivity root email flow activity} is started. * @@ -65,15 +65,15 @@ public class AuthMethodPickerActivity extends AppCompatBase private static final String TAG = "AuthMethodPicker"; private static final int RC_EMAIL_FLOW = 2; private static final int RC_ACCOUNT_LINK = 3; + private ArrayList mIdpProviders; - @Nullable - private SmartLock mSmartLock; + @Nullable private SaveSmartLock mSaveSmartLock; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.auth_method_picker_layout); - mSmartLock = mActivityHelper.getSmartLockInstance(this, TAG); + mSaveSmartLock = mActivityHelper.getSaveSmartLockInstance(); findViewById(R.id.email_provider).setOnClickListener(this); populateIdpList(mActivityHelper.getFlowParams().providerInfo); @@ -173,7 +173,7 @@ public void onSuccess(final IdpResponse response) { .addOnCompleteListener(new CredentialSignInHandler( AuthMethodPickerActivity.this, mActivityHelper, - mSmartLock, + mSaveSmartLock, RC_ACCOUNT_LINK, response)); } @@ -209,6 +209,6 @@ protected void onDestroy() { public static Intent createIntent( Context context, FlowParameters flowParams) { - return ActivityHelper.createBaseIntent(context, AuthMethodPickerActivity.class, flowParams); + return BaseHelper.createBaseIntent(context, AuthMethodPickerActivity.class, flowParams); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandler.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandler.java index bedd21c53..e6404dcc7 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandler.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandler.java @@ -14,17 +14,17 @@ package com.firebase.ui.auth.ui.idp; +import android.app.Activity; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import com.firebase.ui.auth.IdpResponse; -import com.firebase.ui.auth.ui.ActivityHelper; -import com.firebase.ui.auth.ui.AppCompatBase; +import com.firebase.ui.auth.ui.BaseHelper; import com.firebase.ui.auth.ui.TaskFailureLogger; import com.firebase.ui.auth.ui.account_link.WelcomeBackIdpPrompt; import com.firebase.ui.auth.ui.account_link.WelcomeBackPasswordPrompt; -import com.firebase.ui.auth.util.SmartLock; +import com.firebase.ui.auth.util.signincontainer.SaveSmartLock; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; @@ -39,20 +39,20 @@ public class CredentialSignInHandler implements OnCompleteListener { private final static String TAG = "CredentialSignInHandler"; - private AppCompatBase mActivity; - private ActivityHelper mActivityHelper; - @Nullable private SmartLock mSmartLock; + private Activity mActivity; + private BaseHelper mHelper; + @Nullable private SaveSmartLock mSmartLock; private IdpResponse mResponse; private int mAccountLinkResultCode; public CredentialSignInHandler( - AppCompatBase activity, - ActivityHelper activityHelper, - @Nullable SmartLock smartLock, + Activity activity, + BaseHelper helper, + @Nullable SaveSmartLock smartLock, int accountLinkResultCode, IdpResponse response) { mActivity = activity; - mActivityHelper = activityHelper; + mHelper = helper; mSmartLock = smartLock; mResponse = response; mAccountLinkResultCode = accountLinkResultCode; @@ -62,7 +62,7 @@ public CredentialSignInHandler( public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { FirebaseUser firebaseUser = task.getResult().getUser(); - mActivityHelper.saveCredentialsOrFinish( + mHelper.saveCredentialsOrFinish( mSmartLock, mActivity, firebaseUser, @@ -70,7 +70,7 @@ public void onComplete(@NonNull Task task) { } else { if (task.getException() instanceof FirebaseAuthUserCollisionException) { final String email = mResponse.getEmail(); - FirebaseAuth firebaseAuth = mActivityHelper.getFirebaseAuth(); + FirebaseAuth firebaseAuth = mHelper.getFirebaseAuth(); firebaseAuth.fetchProvidersForEmail(email) .addOnFailureListener(new TaskFailureLogger( TAG, "Error fetching providers for email")) @@ -84,7 +84,7 @@ public void onFailure(@NonNull Exception e) { } }); } else { - mActivityHelper.dismissDialog(); + mHelper.dismissDialog(); Log.e(TAG, "Unexpected exception when signing in with credential", task.getException()); } @@ -100,23 +100,23 @@ public StartWelcomeBackFlow(String email) { @Override public void onSuccess(@NonNull ProviderQueryResult result) { - mActivityHelper.dismissDialog(); + mHelper.dismissDialog(); String provider = result.getProviders().get(0); if (provider.equals(EmailAuthProvider.PROVIDER_ID)) { // Start email welcome back flow mActivity.startActivityForResult( WelcomeBackPasswordPrompt.createIntent( - mActivityHelper.getApplicationContext(), - mActivityHelper.getFlowParams(), + mHelper.getApplicationContext(), + mHelper.getFlowParams(), mResponse ), mAccountLinkResultCode); } else { // Start IDP welcome back flow mActivity.startActivityForResult( WelcomeBackIdpPrompt.createIntent( - mActivityHelper.getApplicationContext(), - mActivityHelper.getFlowParams(), + mHelper.getApplicationContext(), + mHelper.getFlowParams(), result.getProviders().get(0), mResponse, mEmail diff --git a/auth/src/main/java/com/firebase/ui/auth/util/CredentialsAPI.java b/auth/src/main/java/com/firebase/ui/auth/util/CredentialsAPI.java deleted file mode 100644 index 8cec9fa25..000000000 --- a/auth/src/main/java/com/firebase/ui/auth/util/CredentialsAPI.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.firebase.ui.auth.util; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.content.IntentSender; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; -import android.widget.Toast; - -import com.google.android.gms.auth.api.Auth; -import com.google.android.gms.auth.api.credentials.Credential; -import com.google.android.gms.auth.api.credentials.CredentialRequest; -import com.google.android.gms.auth.api.credentials.CredentialRequestResult; -import com.google.android.gms.auth.api.credentials.IdentityProviders; -import com.google.android.gms.auth.api.signin.GoogleSignInOptions; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.api.CommonStatusCodes; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.common.api.Status; - -public class CredentialsAPI implements - GoogleApiClient.ConnectionCallbacks, - GoogleApiClient.OnConnectionFailedListener { - private static final int RC_CREDENTIALS_READ = 2; - private static final String TAG = "CredentialsAPI"; - - private GoogleApiClient mGoogleApiClient; - private boolean mAutoSignInAvailable; - private boolean mSignInResolutionNeeded; - private Activity mActivity; - private CredentialRequestResult mCredentialRequestResult; - private ProgressDialog mProgressDialog; - private Credential mCredential; - private final CallbackInterface mCallback; - private PlayServicesHelper mPlayServicesHelper; - - public interface CallbackInterface { - void onAsyncTaskFinished(); - } - - public CredentialsAPI(Activity activity, CallbackInterface callback) { - mAutoSignInAvailable = false; - mSignInResolutionNeeded = false; - mActivity = activity; - mCallback = callback; - mPlayServicesHelper = PlayServicesHelper.getInstance(mActivity); - - initGoogleApiClient(null); - requestCredentials(true /* shouldResolve */, false /* onlyPasswords */); - } - - public boolean isPlayServicesAvailable() { - return mPlayServicesHelper.isPlayServicesAvailable(); - } - - public boolean isCredentialsAvailable() { - // TODO: (serikb) find the way to check if Credentials is available on top of play services - return true; - } - - public boolean isAutoSignInAvailable() { - return mAutoSignInAvailable; - } - - public boolean isSignInResolutionNeeded() { - return mSignInResolutionNeeded; - } - - public void resolveSignIn() { - mSignInResolutionNeeded = false; - } - - public void resolveSavedEmails(Activity activity) { - if (mCredentialRequestResult == null || mCredentialRequestResult.getStatus() == null) { - return; - } - Status status = mCredentialRequestResult.getStatus(); - if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) { - try { - status.startResolutionForResult(activity, RC_CREDENTIALS_READ); - } catch (IntentSender.SendIntentException e) { - Log.e(TAG, "Failed to send Credentials intent.", e); - } - } - } - - public Credential getCredential() { - return mCredential; - } - - public String getEmailFromCredential() { - if (mCredential == null) { - return null; - } - return mCredential.getId(); - } - - public String getAccountTypeFromCredential() { - if (mCredential == null) { - return null; - } - return mCredential.getAccountType(); - } - - public String getPasswordFromCredential() { - if (mCredential == null) { - return null; - } - return mCredential.getPassword(); - } - - @Override - public void onConnected(@Nullable Bundle bundle) {} - - @Override - public void onConnectionSuspended(int cause) {} - - private void initGoogleApiClient(String accountName) { - GoogleSignInOptions.Builder gsoBuilder = new GoogleSignInOptions - .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) - .requestEmail(); - - if (accountName != null) { - gsoBuilder.setAccountName(accountName); - } - - GoogleApiClient.Builder builder = new GoogleApiClient.Builder(mActivity) - .addConnectionCallbacks(this) - .addApi(Auth.CREDENTIALS_API) - .addApi(Auth.GOOGLE_SIGN_IN_API, gsoBuilder.build()); - - mGoogleApiClient = builder.build(); - } - - public void googleSilentSignIn() { - // Try silent sign-in with Google Sign In API - Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient); - } - - public void handleCredential(Credential credential) { - mCredential = credential; - - if (IdentityProviders.GOOGLE.equals(credential.getAccountType())) { - // Google account, rebuild GoogleApiClient to set account name and then try - initGoogleApiClient(credential.getId()); - googleSilentSignIn(); - } else { - // Email/password account - String status = String.format("Signed in as %s", credential.getId()); - Log.d(TAG, status); - } - } - - public void requestCredentials(final boolean shouldResolve, boolean onlyPasswords) { - if (!mPlayServicesHelper.isPlayServicesAvailable()) { - // TODO(samstern): it would probably be better to not actually call the method - // in this case. - return; - } - - CredentialRequest.Builder crBuilder = new CredentialRequest.Builder() - .setPasswordLoginSupported(true); - - if (!onlyPasswords) { - crBuilder.setAccountTypes( - IdentityProviders.GOOGLE, - IdentityProviders.TWITTER, - IdentityProviders.FACEBOOK); - } - - showProgress(); - Auth.CredentialsApi.request(mGoogleApiClient, crBuilder.build()) - .setResultCallback( - new ResultCallback() { - @Override - public void onResult(CredentialRequestResult credentialRequestResult) { - mCredentialRequestResult = credentialRequestResult; - Status status = credentialRequestResult.getStatus(); - - if (status.isSuccess()) { - // Auto sign-in success - mAutoSignInAvailable = true; - handleCredential(credentialRequestResult.getCredential()); - } else if (status.getStatusCode() == - CommonStatusCodes.RESOLUTION_REQUIRED && shouldResolve) { - mSignInResolutionNeeded = true; - // Getting credential needs to show some UI, start resolution - } - hideProgress(); - mCallback.onAsyncTaskFinished(); - } - }); - } - - private void showProgress() { - if (mProgressDialog == null || !mProgressDialog.isShowing()) { - mProgressDialog = new ProgressDialog(mActivity); - mProgressDialog.setIndeterminate(true); - mProgressDialog.setMessage( - mActivity.getString(com.firebase.ui.auth.R.string.progress_dialog_loading)); - } - mProgressDialog.show(); - } - - private void hideProgress() { - if (mProgressDialog != null && mProgressDialog.isShowing()) { - mProgressDialog.dismiss(); - } - } - - public void onStart() { - if (mGoogleApiClient != null) { - mGoogleApiClient.connect(); - } - } - - public void onStop() { - if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) { - mGoogleApiClient.disconnect(); - } - - hideProgress(); - } - - @Override - public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { - Log.w(TAG, "onConnectionFailed:" + connectionResult); - Toast.makeText(mActivity, "An error has occurred.", Toast.LENGTH_SHORT).show(); - } - - public boolean isGoogleApiClient() { - return mGoogleApiClient != null; - } - - public GoogleApiClient getGoogleApiClient() { - return mGoogleApiClient; - } - - public void signOut() { - disableAutoSignIn(); - Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback( - new ResultCallback() { - @Override - public void onResult(Status status) { - mCallback.onAsyncTaskFinished(); - } - }); - } - - public void disableAutoSignIn() { - Auth.CredentialsApi.disableAutoSignIn(mGoogleApiClient); - } -} diff --git a/auth/src/main/java/com/firebase/ui/auth/util/FirebaseAuthWrapperImpl.java b/auth/src/main/java/com/firebase/ui/auth/util/FirebaseAuthWrapperImpl.java index 4e09de2f8..6ad48c846 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/FirebaseAuthWrapperImpl.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/FirebaseAuthWrapperImpl.java @@ -160,7 +160,7 @@ public PendingIntent getEmailHintIntent(FragmentActivity fragmentActivity) { GoogleApiClient client = new GoogleApiClient.Builder(fragmentActivity) .addConnectionCallbacks(this) .addApi(Auth.CREDENTIALS_API) - .enableAutoManage(fragmentActivity, new OnConnectionFailedListener() { + .enableAutoManage(fragmentActivity, GoogleApiConstants.AUTO_MANAGE_ID3, new OnConnectionFailedListener() { @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { Log.e(TAG, "Client connection failed: " + connectionResult.getErrorMessage()); diff --git a/auth/src/main/java/com/firebase/ui/auth/util/GoogleApiConstants.java b/auth/src/main/java/com/firebase/ui/auth/util/GoogleApiConstants.java new file mode 100644 index 000000000..9426b6ff3 --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/util/GoogleApiConstants.java @@ -0,0 +1,8 @@ +package com.firebase.ui.auth.util; + +public class GoogleApiConstants { + public static final int AUTO_MANAGE_ID0 = 0; + public static final int AUTO_MANAGE_ID1 = 1; + public static final int AUTO_MANAGE_ID2 = 2; + public static final int AUTO_MANAGE_ID3 = 3; +} diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/IdpSignInContainerActivity.java b/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/IdpSignInContainer.java similarity index 53% rename from auth/src/main/java/com/firebase/ui/auth/ui/idp/IdpSignInContainerActivity.java rename to auth/src/main/java/com/firebase/ui/auth/util/signincontainer/IdpSignInContainer.java index cf9403b3f..c9acaac46 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/IdpSignInContainerActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/IdpSignInContainer.java @@ -12,27 +12,31 @@ * limitations under the License. */ -package com.firebase.ui.auth.ui.idp; +package com.firebase.ui.auth.util.signincontainer; -import android.content.Context; +import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.util.Log; -import com.firebase.ui.auth.AuthUI.IdpConfig; +import com.firebase.ui.auth.AuthUI; import com.firebase.ui.auth.IdpResponse; +import com.firebase.ui.auth.provider.AuthCredentialHelper; import com.firebase.ui.auth.provider.FacebookProvider; import com.firebase.ui.auth.provider.GoogleProvider; import com.firebase.ui.auth.provider.IdpProvider; import com.firebase.ui.auth.provider.IdpProvider.IdpCallback; import com.firebase.ui.auth.provider.TwitterProvider; -import com.firebase.ui.auth.ui.ActivityHelper; -import com.firebase.ui.auth.ui.AppCompatBase; -import com.firebase.ui.auth.ui.AuthCredentialHelper; +import com.firebase.ui.auth.ui.BaseFragment; import com.firebase.ui.auth.ui.ExtraConstants; import com.firebase.ui.auth.ui.FlowParameters; +import com.firebase.ui.auth.ui.FragmentHelper; import com.firebase.ui.auth.ui.TaskFailureLogger; -import com.firebase.ui.auth.util.SmartLock; +import com.firebase.ui.auth.ui.idp.CredentialSignInHandler; import com.google.android.gms.tasks.Task; import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.AuthResult; @@ -41,25 +45,22 @@ import com.google.firebase.auth.GoogleAuthProvider; import com.google.firebase.auth.TwitterAuthProvider; -public class IdpSignInContainerActivity extends AppCompatBase implements IdpCallback { +public class IdpSignInContainer extends BaseFragment implements IdpCallback { private static final String TAG = "IDPSignInContainer"; private static final int RC_WELCOME_BACK_IDP = 4; private IdpProvider mIdpProvider; - private String mProvider; - private String mEmail; - @Nullable - private SmartLock mSmartLock; + @Nullable private SaveSmartLock mSaveSmartLock; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mSmartLock = mActivityHelper.getSmartLockInstance(this, TAG); - String provider = getIntent().getStringExtra(ExtraConstants.EXTRA_PROVIDER); - String email = getIntent().getStringExtra(ExtraConstants.EXTRA_EMAIL); - IdpConfig providerConfig = null; - for (IdpConfig config : mActivityHelper.getFlowParams().providerInfo) { + mSaveSmartLock = mHelper.getSaveSmartLockInstance(getActivity()); + String email = getArguments().getString(ExtraConstants.EXTRA_EMAIL); + String provider = getArguments().getString(ExtraConstants.EXTRA_PROVIDER); + AuthUI.IdpConfig providerConfig = null; + for (AuthUI.IdpConfig config : mHelper.getFlowParams().providerInfo) { if (config.getProviderId().equalsIgnoreCase(provider)) { providerConfig = config; break; @@ -68,20 +69,20 @@ protected void onCreate(Bundle savedInstanceState) { if (providerConfig == null) { // we don't have a provider to handle this - finish(RESULT_CANCELED, new Intent()); + finish(Activity.RESULT_CANCELED, new Intent()); return; } if (provider.equalsIgnoreCase(FacebookAuthProvider.PROVIDER_ID)) { - mIdpProvider = new FacebookProvider(this, providerConfig); + mIdpProvider = new FacebookProvider(getContext(), providerConfig); } else if (provider.equalsIgnoreCase(GoogleAuthProvider.PROVIDER_ID)) { - mIdpProvider = new GoogleProvider(this, providerConfig, email); + mIdpProvider = new GoogleProvider(getActivity(), providerConfig, email); } else if (provider.equalsIgnoreCase(TwitterAuthProvider.PROVIDER_ID)) { - mIdpProvider = new TwitterProvider(this); + mIdpProvider = new TwitterProvider(getContext()); } mIdpProvider.setAuthenticationCallback(this); - mIdpProvider.startLogin(this); + mIdpProvider.startLogin(getActivity()); } @Override @@ -89,22 +90,22 @@ public void onSuccess(final IdpResponse response) { Intent data = new Intent(); data.putExtra(ExtraConstants.EXTRA_IDP_RESPONSE, response); AuthCredential credential = AuthCredentialHelper.getAuthCredential(response); - final FirebaseAuth firebaseAuth = mActivityHelper.getFirebaseAuth(); + final FirebaseAuth firebaseAuth = mHelper.getFirebaseAuth(); Task authResultTask = firebaseAuth.signInWithCredential(credential); authResultTask .addOnFailureListener( new TaskFailureLogger(TAG, "Failure authenticating with credential")) .addOnCompleteListener(new CredentialSignInHandler( - IdpSignInContainerActivity.this, - mActivityHelper, - mSmartLock, + getActivity(), + mHelper, + mSaveSmartLock, RC_WELCOME_BACK_IDP, response)); } @Override public void onFailure(Bundle extra) { - finish(RESULT_CANCELED, new Intent()); + finish(Activity.RESULT_CANCELED, new Intent()); } @Override @@ -117,16 +118,34 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } } - public static Intent createIntent( - Context context, - FlowParameters flowParams, - String provider, - String email) { - return ActivityHelper.createBaseIntent( - context, - IdpSignInContainerActivity.class, - flowParams) - .putExtra(ExtraConstants.EXTRA_PROVIDER, provider) - .putExtra(ExtraConstants.EXTRA_EMAIL, email); + public static void signIn(FragmentActivity activity, + FlowParameters parameters, + String email, + String provider) { + FragmentManager fm = activity.getSupportFragmentManager(); + Fragment fragment = fm.findFragmentByTag(TAG); + if (fragment == null || !(fragment instanceof IdpSignInContainer)) { + IdpSignInContainer result = new IdpSignInContainer(); + + Bundle bundle = FragmentHelper.getFlowParamsBundle(parameters); + bundle.putString(ExtraConstants.EXTRA_EMAIL, email); + bundle.putString(ExtraConstants.EXTRA_PROVIDER, provider); + result.setArguments(bundle); + + try { + fm.beginTransaction().add(result, TAG).disallowAddToBackStack().commit(); + } catch (IllegalStateException e) { + Log.e(TAG, "Cannot add fragment", e); + } + } + } + + public static IdpSignInContainer getInstance(FragmentActivity activity) { + Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag(TAG); + if (fragment != null && fragment instanceof IdpSignInContainer) { + return (IdpSignInContainer) fragment; + } else { + return null; + } } } diff --git a/auth/src/main/java/com/firebase/ui/auth/util/SmartLock.java b/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SaveSmartLock.java similarity index 60% rename from auth/src/main/java/com/firebase/ui/auth/util/SmartLock.java rename to auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SaveSmartLock.java index 605828a4a..baea249d6 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/SmartLock.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SaveSmartLock.java @@ -12,7 +12,7 @@ * limitations under the License. */ -package com.firebase.ui.auth.util; +package com.firebase.ui.auth.util.signincontainer; import android.app.PendingIntent; import android.content.Intent; @@ -22,46 +22,42 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.AppCompatActivity; +import android.text.TextUtils; import android.util.Log; +import android.widget.Toast; -import com.firebase.ui.auth.BuildConfig; import com.firebase.ui.auth.IdpResponse; -import com.firebase.ui.auth.ui.ActivityHelper; import com.firebase.ui.auth.ui.ExtraConstants; +import com.firebase.ui.auth.ui.FlowParameters; +import com.firebase.ui.auth.ui.FragmentHelper; +import com.firebase.ui.auth.util.GoogleApiConstants; +import com.firebase.ui.auth.util.PlayServicesHelper; import com.google.android.gms.auth.api.Auth; import com.google.android.gms.auth.api.credentials.Credential; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; -import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.GoogleApiClient.Builder; -import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.Status; import com.google.firebase.auth.FirebaseUser; import static android.app.Activity.RESULT_OK; -public class SmartLock extends Fragment - implements GoogleApiClient.ConnectionCallbacks, - GoogleApiClient.OnConnectionFailedListener, - ResultCallback { - private static final String TAG = "SmartLockFragment"; +public class SaveSmartLock extends SmartLockBase { + private static final String TAG = "SaveSmartLock"; private static final int RC_SAVE = 100; private static final int RC_UPDATE_SERVICE = 28; - private ActivityHelper mActivityHelper; private String mName; private String mEmail; private String mPassword; private String mProfilePictureUri; private IdpResponse mResponse; - private GoogleApiClient mCredentialsApiClient; @Override - public void onConnected(@Nullable Bundle bundle) { - if (mEmail == null) { + public void onConnected(Bundle bundle) { + if (TextUtils.isEmpty(mEmail)) { Log.e(TAG, "Unable to save null credential!"); finish(); return; @@ -73,7 +69,7 @@ public void onConnected(@Nullable Bundle bundle) { // only password OR provider can be set, not both if (mResponse != null) { String translatedProvider = - SmartLockUtil.providerIdToAccountType(mResponse.getProviderType()); + providerIdToAccountType(mResponse.getProviderType()); if (translatedProvider != null) { builder.setAccountType(translatedProvider); } else { @@ -92,24 +88,15 @@ public void onConnected(@Nullable Bundle bundle) { builder.setProfilePictureUri(Uri.parse(mProfilePictureUri)); } - mActivityHelper.getCredentialsApi() - .save(mCredentialsApiClient, builder.build()) + mHelper.getCredentialsApi() + .save(mGoogleApiClient, builder.build()) .setResultCallback(this); } - @Override - public void onConnectionSuspended(int i) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Connection suspended with code " + i); - } - } - @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "connection failed with " + connectionResult.getErrorMessage() - + " and code: " + connectionResult.getErrorCode()); - } + Toast.makeText(getActivity(), "An error has occurred.", Toast.LENGTH_SHORT).show(); + PendingIntent resolution = GoogleApiAvailability .getInstance() @@ -117,20 +104,13 @@ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { connectionResult.getErrorCode(), RC_UPDATE_SERVICE); try { - startIntentSenderForResult(resolution.getIntentSender(), - RC_UPDATE_SERVICE, - null, - 0, - 0, - 0, - null); + mHelper.startIntentSenderForResult(resolution.getIntentSender(), RC_UPDATE_SERVICE); } catch (IntentSender.SendIntentException e) { - e.printStackTrace(); + Log.e(TAG, "STATUS: Failed to send resolution.", e); finish(); } } - @Override public void onResult(@NonNull Status status) { if (status.isSuccess()) { @@ -140,13 +120,8 @@ public void onResult(@NonNull Status status) { // Try to resolve the save request. This will prompt the user if // the credential is new. try { - startIntentSenderForResult(status.getResolution().getIntentSender(), - RC_SAVE, - null, - 0, - 0, - 0, - null); + mHelper.startIntentSenderForResult(status.getResolution().getIntentSender(), + RC_SAVE); } catch (IntentSender.SendIntentException e) { // Could not resolve the request Log.e(TAG, "STATUS: Failed to send resolution.", e); @@ -164,21 +139,16 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == RC_SAVE) { - if (resultCode == RESULT_OK) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "SAVE: OK"); - } - finish(); - } else { + if (resultCode != RESULT_OK) { Log.e(TAG, "SAVE: Canceled by user"); - finish(); } + finish(); } else if (requestCode == RC_UPDATE_SERVICE) { if (resultCode == RESULT_OK) { Credential credential = new Credential.Builder(mEmail).setPassword(mPassword) .build(); - mActivityHelper.getCredentialsApi() - .save(mCredentialsApiClient, credential) + mHelper.getCredentialsApi() + .save(mGoogleApiClient, credential) .setResultCallback(this); } else { Log.e(TAG, "SAVE: Canceled by user"); @@ -189,24 +159,30 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { private void finish() { Intent resultIntent = new Intent().putExtra(ExtraConstants.EXTRA_IDP_RESPONSE, mResponse); - mActivityHelper.finish(RESULT_OK, resultIntent); + finish(RESULT_OK, resultIntent); } /** * If SmartLock is enabled and Google Play Services is available, save the credentials. * Otherwise, finish the calling Activity with RESULT_OK. * - * @param activity the calling Activity. + * Note: saveCredentialsOrFinish cannot be called immediately after getInstance because + * onCreate has not yet been called. + * * @param firebaseUser Firebase user to save in Credential. * @param password (optional) password for email credential. * @param response (optional) an {@link IdpResponse} representing the result of signing in. */ - public void saveCredentialsOrFinish(AppCompatActivity activity, - ActivityHelper helper, - FirebaseUser firebaseUser, + public void saveCredentialsOrFinish(FirebaseUser firebaseUser, @Nullable String password, @Nullable IdpResponse response) { - mActivityHelper = helper; + if (!mHelper.getFlowParams().smartLockEnabled + || !PlayServicesHelper.getInstance(getContext()).isPlayServicesAvailable() + || getActivity().isFinishing()) { + finish(); + return; + } + mName = firebaseUser.getDisplayName(); mEmail = firebaseUser.getEmail(); mPassword = password; @@ -214,57 +190,32 @@ public void saveCredentialsOrFinish(AppCompatActivity activity, mProfilePictureUri = firebaseUser.getPhotoUrl() != null ? firebaseUser.getPhotoUrl() .toString() : null; - // If SmartLock is disabled, finish the Activity - if (!helper.getFlowParams().smartLockEnabled) { - finish(); - return; - } - - // If Play Services is not available, finish the Activity - if (!PlayServicesHelper.getInstance(activity).isPlayServicesAvailable()) { - finish(); - return; - } - - if (!FirebaseAuthWrapperFactory - .getFirebaseAuthWrapper(helper.getFlowParams().appName) - .isPlayServicesAvailable(activity)) { - finish(); - return; - } - - if (activity.isFinishing()) { - finish(); - return; - } - - mCredentialsApiClient = new Builder(activity) + mGoogleApiClient = new Builder(getContext().getApplicationContext()) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(Auth.CREDENTIALS_API) - .enableAutoManage(activity, this) + .enableAutoManage(getActivity(), GoogleApiConstants.AUTO_MANAGE_ID2, this) .build(); - mCredentialsApiClient.connect(); + mGoogleApiClient.connect(); } @Nullable - public static SmartLock getInstance(AppCompatActivity activity, String tag) { - SmartLock result; + public static SaveSmartLock getInstance(FragmentActivity activity, FlowParameters parameters) { + SaveSmartLock result; FragmentManager fm = activity.getSupportFragmentManager(); - FragmentTransaction ft = fm.beginTransaction(); - - Fragment fragment = fm.findFragmentByTag(tag); - if (fragment == null || !(fragment instanceof SmartLock)) { - result = new SmartLock(); + Fragment fragment = fm.findFragmentByTag(TAG); + if (fragment == null || !(fragment instanceof SaveSmartLock)) { + result = new SaveSmartLock(); + result.setArguments(FragmentHelper.getFlowParamsBundle(parameters)); try { - ft.add(result, tag).disallowAddToBackStack().commit(); + fm.beginTransaction().add(result, TAG).disallowAddToBackStack().commit(); } catch (IllegalStateException e) { Log.e(TAG, "Cannot add fragment", e); return null; } } else { - result = (SmartLock) fragment; + result = (SaveSmartLock) fragment; } return result; diff --git a/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SignInDelegate.java b/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SignInDelegate.java new file mode 100644 index 000000000..ed3799aa3 --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SignInDelegate.java @@ -0,0 +1,381 @@ +package com.firebase.ui.auth.util.signincontainer; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentSender; +import android.net.ConnectivityManager; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.text.TextUtils; +import android.util.Log; + +import com.firebase.ui.auth.AuthUI; +import com.firebase.ui.auth.R; +import com.firebase.ui.auth.ui.ExtraConstants; +import com.firebase.ui.auth.ui.FlowParameters; +import com.firebase.ui.auth.ui.FragmentHelper; +import com.firebase.ui.auth.ui.TaskFailureLogger; +import com.firebase.ui.auth.ui.email.SignInNoPasswordActivity; +import com.firebase.ui.auth.ui.idp.AuthMethodPickerActivity; +import com.firebase.ui.auth.util.CredentialsApiHelper; +import com.firebase.ui.auth.util.EmailFlowUtil; +import com.firebase.ui.auth.util.GoogleApiConstants; +import com.firebase.ui.auth.util.PlayServicesHelper; +import com.google.android.gms.auth.api.Auth; +import com.google.android.gms.auth.api.credentials.Credential; +import com.google.android.gms.auth.api.credentials.CredentialRequest; +import com.google.android.gms.auth.api.credentials.CredentialRequestResult; +import com.google.android.gms.auth.api.credentials.IdentityProviders; +import com.google.android.gms.common.api.CommonStatusCodes; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.Status; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.android.gms.tasks.Task; +import com.google.firebase.auth.AuthResult; +import com.google.firebase.auth.EmailAuthProvider; +import com.google.firebase.auth.FacebookAuthProvider; +import com.google.firebase.auth.FirebaseAuthInvalidUserException; +import com.google.firebase.auth.GoogleAuthProvider; +import com.google.firebase.auth.TwitterAuthProvider; + +import java.util.ArrayList; +import java.util.List; + +import static android.app.Activity.RESULT_CANCELED; +import static android.app.Activity.RESULT_OK; +import static com.firebase.ui.auth.ui.ResultCodes.RESULT_NO_NETWORK; + +/** + * Attempts to acquire a credential from Smart Lock for Passwords to sign in + * an existing account. If this succeeds, an attempt is made to sign the user in + * with this credential. If it does not, the + * {@link AuthMethodPickerActivity authentication method picker activity} + * is started, unless only email is supported, in which case the + * {@link SignInNoPasswordActivity email sign-in flow} + * is started. + */ +public class SignInDelegate extends SmartLockBase { + private static final String TAG = "SignInDelegate"; + private static final int RC_CREDENTIALS_READ = 2; + private static final int RC_IDP_SIGNIN = 3; + private static final int RC_AUTH_METHOD_PICKER = 4; + private static final int RC_EMAIL_FLOW = 5; + private static final int RC_PLAY_SERVICES = 6; + + private Credential mCredential; + + @Override + public void onCreate(Bundle savedInstance) { + super.onCreate(savedInstance); + if (savedInstance != null) { + // We already have a running instance of this fragment + return; + } + + if (!hasNetworkConnection()) { + Log.d(TAG, "No network connection"); + finish(RESULT_NO_NETWORK, new Intent()); + return; + } + + // Make Google Play Services available at the correct version, if possible + boolean madeAvailable = + PlayServicesHelper + .getInstance(getActivity()) + .makePlayServicesAvailable(getActivity(), RC_PLAY_SERVICES, + new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialogInterface) { + Log.w(TAG, + "playServices:dialog.onCancel()"); + finish(RESULT_CANCELED, new Intent()); + } + }); + + if (!madeAvailable + || !PlayServicesHelper.getInstance(getActivity()).isPlayServicesAvailable()) { + Log.w(TAG, "playServices: could not make available."); + finish(RESULT_CANCELED, new Intent()); + return; + } + + if (mHelper.getFlowParams().smartLockEnabled) { + mHelper.showLoadingDialog(R.string.progress_dialog_loading); + + mGoogleApiClient = new GoogleApiClient.Builder(getContext().getApplicationContext()) + .addConnectionCallbacks(this) + .addApi(Auth.CREDENTIALS_API) + .enableAutoManage(getActivity(), GoogleApiConstants.AUTO_MANAGE_ID1, this) + .build(); + mGoogleApiClient.connect(); + + mHelper.getCredentialsApi() + .request(mGoogleApiClient, + new CredentialRequest.Builder() + .setPasswordLoginSupported(true) + .setAccountTypes(getSupportedAccountTypes().toArray(new String[0])) + .build()) + .setResultCallback(this); + } else { + startAuthMethodChoice(); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + // It doesn't matter what we put here, we just don't want outState to be empty + outState.putBoolean(ExtraConstants.HAS_EXISTING_INSTANCE, true); + super.onSaveInstanceState(outState); + } + + @Override + public void onResult(@NonNull CredentialRequestResult result) { + Status status = result.getStatus(); + + if (status.isSuccess()) { + // Auto sign-in success + handleCredential(result.getCredential()); + return; + } else if (status.hasResolution()) { + try { + if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) { + mHelper.startIntentSenderForResult( + status.getResolution().getIntentSender(), + RC_CREDENTIALS_READ); + return; + } else if (!getSupportedAccountTypes().isEmpty()) { + mHelper.startIntentSenderForResult( + status.getResolution().getIntentSender(), + RC_CREDENTIALS_READ); + return; + } + } catch (IntentSender.SendIntentException e) { + Log.e(TAG, "Failed to send Credentials intent.", e); + } + } + startAuthMethodChoice(); + } + + @Override + public void onConnected(@Nullable Bundle bundle) { + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case RC_CREDENTIALS_READ: + if (resultCode == RESULT_OK) { + // credential selected from SmartLock, log in with that credential + Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY); + handleCredential(credential); + } else { + // Smart lock selector cancelled, go to the AuthMethodPicker screen + startAuthMethodChoice(); + } + break; + case RC_IDP_SIGNIN: + case RC_AUTH_METHOD_PICKER: + case RC_EMAIL_FLOW: + finish(resultCode, data); + break; + case RC_PLAY_SERVICES: + if (resultCode != RESULT_OK) finish(resultCode, data); + break; + default: + IdpSignInContainer signInContainer = IdpSignInContainer.getInstance(getActivity()); + if (signInContainer != null) { + signInContainer.onActivityResult(requestCode, resultCode, data); + } + } + } + + private List getSupportedAccountTypes() { + List accounts = new ArrayList<>(); + for (AuthUI.IdpConfig idpConfig : mHelper.getFlowParams().providerInfo) { + String providerId = idpConfig.getProviderId(); + if (providerId.equals(GoogleAuthProvider.PROVIDER_ID) + || providerId.equals(FacebookAuthProvider.PROVIDER_ID) + || providerId.equals(TwitterAuthProvider.PROVIDER_ID)) { + accounts.add(providerIdToAccountType(providerId)); + } + } + return accounts; + } + + private String getEmailFromCredential() { + if (mCredential == null) { + return null; + } + return mCredential.getId(); + } + + private String getAccountTypeFromCredential() { + if (mCredential == null) { + return null; + } + return mCredential.getAccountType(); + } + + private String getPasswordFromCredential() { + if (mCredential == null) { + return null; + } + return mCredential.getPassword(); + } + + private void handleCredential(Credential credential) { + mCredential = credential; + String email = getEmailFromCredential(); + String password = getPasswordFromCredential(); + if (!TextUtils.isEmpty(email)) { + if (TextUtils.isEmpty(password)) { + // log in with id/provider + redirectToIdpSignIn(email, getAccountTypeFromCredential()); + } else { + // Sign in with the email/password retrieved from SmartLock + signInWithEmailAndPassword(email, password); + } + } + } + + private void startAuthMethodChoice() { + List idpConfigs = mHelper.getFlowParams().providerInfo; + + // If the only provider is Email, immediately launch the email flow. Otherwise, launch + // the auth method picker screen. + if (idpConfigs.size() == 1) { + if (idpConfigs.get(0).getProviderId().equals(EmailAuthProvider.PROVIDER_ID)) { + startActivityForResult( + EmailFlowUtil.createIntent( + getContext(), + mHelper.getFlowParams()), + RC_EMAIL_FLOW); + } else { + redirectToIdpSignIn(null, + providerIdToAccountType(idpConfigs.get(0).getProviderId())); + } + } else { + startActivityForResult( + AuthMethodPickerActivity.createIntent( + getContext(), + mHelper.getFlowParams()), + RC_AUTH_METHOD_PICKER); + } + mHelper.dismissDialog(); + } + + /** + * Begin sign in process with email and password from a SmartLock credential. + * On success, finish with {@code RESULT_OK}. + * On failure, delete the credential from SmartLock (if applicable) and then launch the + * auth method picker flow. + */ + private void signInWithEmailAndPassword(String email, String password) { + mHelper.getFirebaseAuth() + .signInWithEmailAndPassword(email, password) + .addOnFailureListener(new TaskFailureLogger( + TAG, "Error signing in with email and password")) + .addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(AuthResult authResult) { + finish(RESULT_OK, new Intent()); + } + }) + .addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + if (e instanceof FirebaseAuthInvalidUserException) { + // In this case the credential saved in SmartLock was not + // a valid credential, we should delete it from SmartLock + // before continuing. + deleteCredentialAndRedirect(); + } else { + startAuthMethodChoice(); + } + } + }); + } + + /** + * Delete the last credential retrieved from SmartLock and then redirect to the + * auth method choice flow. + */ + private void deleteCredentialAndRedirect() { + if (mCredential == null) { + Log.w(TAG, "deleteCredentialAndRedirect: null credential"); + startAuthMethodChoice(); + return; + } + + CredentialsApiHelper credentialsApiHelper = CredentialsApiHelper.getInstance(getActivity()); + credentialsApiHelper.delete(mCredential) + .addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (!task.isSuccessful()) { + Log.w(TAG, "deleteCredential:failure", task.getException()); + } + startAuthMethodChoice(); + } + }); + } + + private void redirectToIdpSignIn(String email, String accountType) { + if (accountType != null && (accountType.equals(IdentityProviders.GOOGLE) + || accountType.equals(IdentityProviders.FACEBOOK) + || accountType.equals(IdentityProviders.TWITTER))) { + IdpSignInContainer.signIn( + getActivity(), + mHelper.getFlowParams(), + email, + accountTypeToProviderId(accountType)); + } else { + Log.w(TAG, "unknown provider: " + accountType); + startActivityForResult( + AuthMethodPickerActivity.createIntent( + getContext(), + mHelper.getFlowParams()), + RC_IDP_SIGNIN); + mHelper.dismissDialog(); + } + } + + /** + * Check if there is an active or soon-to-be-active network connection. + */ + private boolean hasNetworkConnection() { + ConnectivityManager manager = + (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); + + return manager != null + && manager.getActiveNetworkInfo() != null + && manager.getActiveNetworkInfo().isConnectedOrConnecting(); + } + + public static void delegate(FragmentActivity activity, FlowParameters params) { + FragmentManager fm = activity.getSupportFragmentManager(); + Fragment fragment = fm.findFragmentByTag(TAG); + if (fragment == null || !(fragment instanceof SignInDelegate)) { + SignInDelegate result = new SignInDelegate(); + result.setArguments(FragmentHelper.getFlowParamsBundle(params)); + fm.beginTransaction().add(result, TAG).disallowAddToBackStack().commit(); + } + } + + public static SignInDelegate getInstance(FragmentActivity activity) { + Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag(TAG); + if (fragment != null && fragment instanceof SignInDelegate) { + return (SignInDelegate) fragment; + } else { + return null; + } + } +} diff --git a/auth/src/main/java/com/firebase/ui/auth/util/SmartLockUtil.java b/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SmartLockBase.java similarity index 52% rename from auth/src/main/java/com/firebase/ui/auth/util/SmartLockUtil.java rename to auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SmartLockBase.java index 4fd61de00..fc302b68e 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/SmartLockUtil.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SmartLockBase.java @@ -1,11 +1,19 @@ -package com.firebase.ui.auth.util; +package com.firebase.ui.auth.util.signincontainer; +import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; +import android.widget.Toast; +import com.firebase.ui.auth.ui.BaseFragment; import com.google.android.gms.auth.api.credentials.Credential; import com.google.android.gms.auth.api.credentials.IdentityProviders; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.Result; +import com.google.android.gms.common.api.ResultCallback; import com.google.firebase.auth.EmailAuthProvider; import com.google.firebase.auth.FacebookAuthProvider; import com.google.firebase.auth.FirebaseUser; @@ -17,11 +25,58 @@ import java.util.Collections; import java.util.List; -/** - * Helper class to deal with Smartlock Flows. - */ -public class SmartLockUtil { - private static final String TAG = "SmartLockUtil"; +public abstract class SmartLockBase extends BaseFragment implements + GoogleApiClient.ConnectionCallbacks, + ResultCallback, + GoogleApiClient.OnConnectionFailedListener { + private static final String TAG = "SmartLockBase"; + + protected GoogleApiClient mGoogleApiClient; + private boolean mWasProgressDialogShowing = false; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + } + + @Override + public void onStart() { + super.onStart(); + if (mWasProgressDialogShowing) { + mHelper.showLoadingDialog(com.firebase.ui.auth.R.string.progress_dialog_loading); + mWasProgressDialogShowing = false; + } + } + + @Override + public void onStop() { + super.onStop(); + mWasProgressDialogShowing = mHelper.isProgressDialogShowing(); + mHelper.dismissDialog(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + cleanup(); + } + + public void cleanup() { + if (mGoogleApiClient != null) { + mGoogleApiClient.disconnect(); + } + } + + @Override + public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { + Toast.makeText(getContext(), "An error has occurred.", Toast.LENGTH_SHORT).show(); + } + + @Override + public void onConnectionSuspended(int i) { + // Just wait + } /** * Translate a Firebase Auth provider ID (such as {@link GoogleAuthProvider#PROVIDER_ID}) to @@ -38,9 +93,22 @@ public static String providerIdToAccountType(@NonNull String providerId) { case EmailAuthProvider.PROVIDER_ID: // The account type for email/password creds is null return null; + default: + return null; } + } - return null; + public static String accountTypeToProviderId(@NonNull String accountType) { + switch (accountType) { + case IdentityProviders.GOOGLE: + return GoogleAuthProvider.PROVIDER_ID; + case IdentityProviders.FACEBOOK: + return FacebookAuthProvider.PROVIDER_ID; + case IdentityProviders.TWITTER: + return TwitterAuthProvider.PROVIDER_ID; + default: + return null; + } } /** diff --git a/auth/src/main/res/layout/confirm_recovery_layout.xml b/auth/src/main/res/layout/confirm_recovery_layout.xml deleted file mode 100644 index aa6013808..000000000 --- a/auth/src/main/res/layout/confirm_recovery_layout.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - -