diff --git a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java index 017c688d2..52843f8ae 100644 --- a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java +++ b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java @@ -29,8 +29,8 @@ import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.RadioButton; - import android.widget.TextView; + import com.firebase.ui.auth.AuthUI; import com.firebase.ui.auth.AuthUI.IdpConfig; import com.firebase.ui.auth.IdpResponse; @@ -47,18 +47,10 @@ import butterknife.ButterKnife; import butterknife.OnClick; -public class AuthUiActivity extends AppCompatActivity { - - private static final String TAG = "AuthUIActivity"; - +public class AuthUiActivity extends AppCompatActivity implements AuthUI.SignInResult { private static final String UNCHANGED_CONFIG_VALUE = "CHANGE-ME"; - - private static final String GOOGLE_TOS_URL = - "https://www.google.com/policies/terms/"; - private static final String FIREBASE_TOS_URL = - "https://www.firebase.com/terms/terms-of-service.html"; - - private static final int RC_SIGN_IN = 100; + private static final String GOOGLE_TOS_URL = "https://www.google.com/policies/terms/"; + private static final String FIREBASE_TOS_URL = "https://www.firebase.com/terms/terms-of-service.html"; @BindView(R.id.default_theme) RadioButton mUseDefaultTheme; @@ -183,26 +175,13 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { @OnClick(R.id.sign_in) public void signIn(View view) { - startActivityForResult( - AuthUI.getInstance().createSignInIntentBuilder() - .setTheme(getSelectedTheme()) - .setLogo(getSelectedLogo()) - .setProviders(getSelectedProviders()) - .setTosUrl(getSelectedTosUrl()) - .setIsSmartLockEnabled(mEnableSmartLock.isChecked()) - .build(), - RC_SIGN_IN); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == RC_SIGN_IN) { - handleSignInResponse(resultCode, data); - return; - } - - showSnackbar(R.string.unknown_response); + AuthUI.getInstance().createSignInIntentBuilder() + .setTheme(getSelectedTheme()) + .setLogo(getSelectedLogo()) + .setProviders(getSelectedProviders()) + .setTosUrl(getSelectedTosUrl()) + .setIsSmartLockEnabled(mEnableSmartLock.isChecked()) + .build(this); } @@ -221,13 +200,14 @@ private void setFacebookScopesEnabled(boolean enabled) { } @MainThread - private void handleSignInResponse(int resultCode, Intent data) { - if (resultCode == RESULT_OK) { - startActivity(SignedInActivity.createIntent(this, IdpResponse.fromResultIntent(data))); - finish(); - return; - } + @Override + public void onSignInSuccessful(Intent data) { + startActivity(SignedInActivity.createIntent(this, IdpResponse.fromResultIntent(data))); + finish(); + } + @Override + public void onSignInFailed(int resultCode) { if (resultCode == RESULT_CANCELED) { showSnackbar(R.string.sign_in_cancelled); return; diff --git a/app/src/main/res/fabric.properties b/app/src/main/res/fabric.properties deleted file mode 100644 index 4824fe9f3..000000000 --- a/app/src/main/res/fabric.properties +++ /dev/null @@ -1,2 +0,0 @@ -apiSecret=YOUR_BUILD_SECRET -apiKey=YOUR_API_KEY \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c13bde216..f43f346e1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,8 +32,6 @@ Google TOS Firebase TOS Terms of Service URL: - Unexpected onActivityResult response code - OK Unknown response from AuthUI sign-in Sign in cancelled No internet connection diff --git a/auth/build.gradle b/auth/build.gradle index ed3fb5830..70d36ec89 100644 --- a/auth/build.gradle +++ b/auth/build.gradle @@ -37,9 +37,9 @@ dependencies { // See https://github.com/robolectric/robolectric/issues/1932#issuecomment-219796474 testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1' - compile 'com.facebook.android:facebook-android-sdk:4.14.1' - compile("com.twitter.sdk.android:twitter:2.0.0@aar") { - transitive = true; + compile 'com.facebook.android:facebook-android-sdk:4.16.1' + compile("com.twitter.sdk.android:twitter:2.1.1@aar") { + transitive = true } compile "com.android.support:design:${project.ext.support_library_version}" diff --git a/auth/src/main/AndroidManifest.xml b/auth/src/main/AndroidManifest.xml index 64ed09baf..cd83ce4e0 100644 --- a/auth/src/main/AndroidManifest.xml +++ b/auth/src/main/AndroidManifest.xml @@ -14,10 +14,6 @@ android:name="com.firebase.ui.auth.ui.email.ConfirmRecoverPasswordActivity" android:label="@string/title_confirm_recover_password_activity" android:theme="@style/FirebaseUI.Dialog" /> - - - + android:theme="@style/FirebaseUI.FacebookTranslucent" /> */ public class AuthUI { + public interface SignInResult { + void onSignInSuccessful(Intent data); + + void onSignInFailed(int resultCode); + } /** * Provider identifier for email and password credentials, for use with @@ -325,6 +331,7 @@ public Void then(@NonNull Task task) throws Exception { * API. Returns a {@code Task} that succeeds if the Firebase Auth user deletion succeeds and * fails if the Firebase Auth deletion fails. Credentials deletion failures are handled * silently. + * * @param activity the calling {@link Activity}. */ public Task delete(@NonNull Activity activity) { @@ -343,7 +350,7 @@ public Task delete(@NonNull Activity activity) { CredentialsApiHelper credentialHelper = CredentialsApiHelper.getInstance(gacHelper); // Get all SmartLock credentials associated with the user - List credentials = SmartLockUtil.credentialsFromFirebaseUser(firebaseUser); + List credentials = SmartLock.credentialsFromFirebaseUser(firebaseUser); // For each Credential in the list, create a task to delete it. List> credentialTasks = new ArrayList<>(); @@ -381,6 +388,7 @@ public SignInIntentBuilder createSignInIntentBuilder() { /** * Retrieves the {@link AuthUI} instance associated with the default app, as returned by * {@code FirebaseApp.getInstance()}. + * * @throws IllegalStateException if the default app is not initialized. */ public static AuthUI getInstance() { @@ -406,7 +414,8 @@ public static AuthUI getInstance(FirebaseApp app) { * Default theme used by {@link SignInIntentBuilder#setTheme(int)} if no theme * customization is required. */ - public static @StyleRes int getDefaultTheme() { + @StyleRes + public static int getDefaultTheme() { // TODO(iainmgin): figure out why this works as a static method but not as a static // final variable. return R.style.FirebaseUI; @@ -471,6 +480,7 @@ public static class Builder { /** * Builds the configuration parameters for an identity provider. + * * @param providerId An ID of one of the supported identity providers. e.g. * {@link AuthUI#GOOGLE_PROVIDER}. See {@link AuthUI#SUPPORTED_PROVIDERS} for the * complete list of supported Identity providers @@ -568,7 +578,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); @@ -602,7 +613,7 @@ public SignInIntentBuilder setProviders(@NonNull String... providers) { /** * Enables or disables the use of Smart Lock for Passwords in the sign in flow. - * + * *

SmartLock is enabled by default */ public SignInIntentBuilder setIsSmartLockEnabled(boolean enabled) { @@ -610,24 +621,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)) { @@ -636,5 +629,18 @@ private boolean isIdpAlreadyConfigured(@NonNull String providerId) { } return false; } + + public void build(@NonNull FragmentActivity activity) { + SignInDelegate.delegateSignIn(activity, getFlowParams()); + } + + 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/IdpResponse.java b/auth/src/main/java/com/firebase/ui/auth/IdpResponse.java index 89d98e7e0..b9383de17 100644 --- a/auth/src/main/java/com/firebase/ui/auth/IdpResponse.java +++ b/auth/src/main/java/com/firebase/ui/auth/IdpResponse.java @@ -18,7 +18,15 @@ import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.Nullable; + +import com.firebase.ui.auth.provider.FacebookProvider; +import com.firebase.ui.auth.provider.GoogleProvider; +import com.firebase.ui.auth.provider.TwitterProvider; import com.firebase.ui.auth.ui.ExtraConstants; +import com.google.firebase.auth.AuthCredential; +import com.google.firebase.auth.FacebookAuthProvider; +import com.google.firebase.auth.GoogleAuthProvider; +import com.google.firebase.auth.TwitterAuthProvider; /** * A container that encapsulates the result of authenticating with an Identity Provider. @@ -120,4 +128,18 @@ public void writeToParcel(Parcel dest, int flags) { public static IdpResponse fromResultIntent(Intent resultIntent) { return resultIntent.getParcelableExtra(ExtraConstants.EXTRA_IDP_RESPONSE); } + + public static AuthCredential createCredential(IdpResponse idpSignInResponse) { + if (idpSignInResponse.getProviderType().equalsIgnoreCase(FacebookAuthProvider.PROVIDER_ID)) { + return FacebookProvider.createAuthCredential(idpSignInResponse); + } else if (idpSignInResponse.getProviderType().equalsIgnoreCase(GoogleAuthProvider + .PROVIDER_ID)) { + return GoogleProvider.createAuthCredential(idpSignInResponse); + } else if (idpSignInResponse + .getProviderType() + .equalsIgnoreCase(TwitterAuthProvider.PROVIDER_ID)) { + return TwitterProvider.createAuthCredential(idpSignInResponse); + } + return null; + } } diff --git a/auth/src/main/java/com/firebase/ui/auth/provider/FacebookProvider.java b/auth/src/main/java/com/firebase/ui/auth/provider/FacebookProvider.java index 7fc91c0f0..b18ee0c4d 100644 --- a/auth/src/main/java/com/firebase/ui/auth/provider/FacebookProvider.java +++ b/auth/src/main/java/com/firebase/ui/auth/provider/FacebookProvider.java @@ -19,6 +19,7 @@ import android.content.Intent; import android.os.Bundle; import android.util.Log; + import com.facebook.CallbackManager; import com.facebook.FacebookCallback; import com.facebook.FacebookException; @@ -34,11 +35,13 @@ import com.firebase.ui.auth.R; import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.FacebookAuthProvider; -import java.util.ArrayList; -import java.util.List; + import org.json.JSONException; import org.json.JSONObject; +import java.util.ArrayList; +import java.util.List; + public class FacebookProvider implements IdpProvider, FacebookCallback { protected static final String ERROR = "err"; protected static final String ERROR_MSG = "err_msg"; 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 cb23873ab..6ec7a173b 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 @@ -20,11 +20,10 @@ 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.text.TextUtils; import android.util.Log; -import android.view.View; -import android.view.View.OnClickListener; import com.firebase.ui.auth.AuthUI.IdpConfig; import com.firebase.ui.auth.IdpResponse; @@ -39,24 +38,22 @@ import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.GoogleAuthProvider; -public class GoogleProvider implements - IdpProvider, OnClickListener, GoogleApiClient.OnConnectionFailedListener { +public class GoogleProvider implements IdpProvider, 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; - private Activity mActivity; private IdpCallback mIDPCallback; public GoogleProvider(FragmentActivity activity, IdpConfig idpConfig) { this(activity, idpConfig, null); } - public GoogleProvider(FragmentActivity activity, IdpConfig idpConfig, @Nullable String email) { - mActivity = activity; - String mClientId = activity.getString(R.string.default_web_client_id); + public GoogleProvider(Context context, IdpConfig idpConfig, @Nullable String email) { + String mClientId = context.getString(R.string.default_web_client_id); GoogleSignInOptions googleSignInOptions; GoogleSignInOptions.Builder builder = @@ -64,8 +61,8 @@ public GoogleProvider(FragmentActivity activity, IdpConfig idpConfig, @Nullable .requestEmail() .requestIdToken(mClientId); - if (activity.getResources().getIdentifier( - "google_permissions", "array", activity.getPackageName()) != 0){ + if (context.getResources().getIdentifier( + "google_permissions", "array", context.getPackageName()) != 0) { Log.w(TAG, "DEVELOPER WARNING: You have defined R.array.google_permissions but that is" + " no longer respected as of FirebaseUI 1.0.0. Please see README for IDP scope" + " configuration instructions."); @@ -81,10 +78,15 @@ public GoogleProvider(FragmentActivity activity, IdpConfig idpConfig, @Nullable } googleSignInOptions = builder.build(); - mGoogleApiClient = new GoogleApiClient.Builder(activity) - .enableAutoManage(activity, AUTO_MANAGE_ID, this) - .addApi(Auth.GOOGLE_SIGN_IN_API, googleSignInOptions) - .build(); + GoogleApiClient.Builder googleBuilder = new GoogleApiClient.Builder(context); + + try { + googleBuilder = + googleBuilder.enableAutoManage((FragmentActivity) context, AUTO_MANAGE_ID, this); + } finally { + mGoogleApiClient = + googleBuilder.addApi(Auth.GOOGLE_SIGN_IN_API, googleSignInOptions).build(); + } } public String getName(Context context) { @@ -136,8 +138,15 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { @Override public void startLogin(Activity activity) { - Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); - activity.startActivityForResult(signInIntent, RC_SIGN_IN); + activity.startActivityForResult(getSignInIntent(), RC_SIGN_IN); + } + + public void startLogin(Fragment fragment) { + fragment.startActivityForResult(getSignInIntent(), RC_SIGN_IN); + } + + private Intent getSignInIntent() { + return Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); } private void onError(String errorMessage) { @@ -146,12 +155,6 @@ private void onError(String errorMessage) { mIDPCallback.onFailure(extra); } - @Override - public void onClick(View view) { - Auth.GoogleSignInApi.signOut(mGoogleApiClient); - startLogin(mActivity); - } - @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { Log.w(TAG, "onConnectionFailed:" + connectionResult); diff --git a/auth/src/main/java/com/firebase/ui/auth/provider/IdpProvider.java b/auth/src/main/java/com/firebase/ui/auth/provider/IdpProvider.java index 2a117c30e..0c589f2ed 100644 --- a/auth/src/main/java/com/firebase/ui/auth/provider/IdpProvider.java +++ b/auth/src/main/java/com/firebase/ui/auth/provider/IdpProvider.java @@ -18,6 +18,7 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; + import com.firebase.ui.auth.IdpResponse; public interface IdpProvider { @@ -35,8 +36,8 @@ public interface IdpProvider { void startLogin(Activity activity); - public interface IdpCallback { - public void onSuccess(IdpResponse idpResponse); - public void onFailure(Bundle extra); + interface IdpCallback { + void onSuccess(IdpResponse idpResponse); + void onFailure(Bundle extra); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/AcquireEmailHelper.java b/auth/src/main/java/com/firebase/ui/auth/ui/AcquireEmailHelper.java index 4c11d904d..87ddcd3c3 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/AcquireEmailHelper.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/AcquireEmailHelper.java @@ -16,11 +16,13 @@ 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.account_link.WelcomeBackIdpPrompt; import com.firebase.ui.auth.ui.email.RegisterEmailActivity; import com.firebase.ui.auth.ui.email.SignInActivity; +import com.firebase.ui.auth.util.BaseHelper; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; import com.google.firebase.auth.EmailAuthProvider; @@ -41,16 +43,16 @@ public class AcquireEmailHelper { RC_SIGN_IN ); - private ActivityHelper mActivityHelper; + private BaseHelper mHelper; - public AcquireEmailHelper(ActivityHelper activityHelper) { - mActivityHelper = activityHelper; + public AcquireEmailHelper(BaseHelper helper) { + mHelper = helper; } public void checkAccountExists(final String email) { - FirebaseAuth firebaseAuth = mActivityHelper.getFirebaseAuth(); - mActivityHelper.showLoadingDialog(R.string.progress_dialog_loading); - if (email != null && !email.isEmpty()) { + FirebaseAuth firebaseAuth = mHelper.getFirebaseAuth(); + mHelper.showLoadingDialog(R.string.progress_dialog_loading); + if (!TextUtils.isEmpty(email)) { firebaseAuth .fetchProvidersForEmail(email) .addOnFailureListener( @@ -62,7 +64,7 @@ public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { startEmailHandler(email, task.getResult().getProviders()); } else { - mActivityHelper.dismissDialog(); + mHelper.dismissDialog(); } } }); @@ -70,46 +72,46 @@ public void onComplete(@NonNull Task task) { } private void startEmailHandler(String email, List providers) { - mActivityHelper.dismissDialog(); + mHelper.dismissDialog(); if (providers == null || providers.isEmpty()) { // account doesn't exist yet Intent registerIntent = RegisterEmailActivity.createIntent( - mActivityHelper.getApplicationContext(), - mActivityHelper.getFlowParams(), + mHelper.getApplicationContext(), + mHelper.getFlowParams(), email); - mActivityHelper.startActivityForResult(registerIntent, RC_REGISTER_ACCOUNT); + mHelper.startActivityForResult(registerIntent, RC_REGISTER_ACCOUNT); } else { // account does exist for (String provider : providers) { if (provider.equalsIgnoreCase(EmailAuthProvider.PROVIDER_ID)) { Intent signInIntent = SignInActivity.createIntent( - mActivityHelper.getApplicationContext(), - mActivityHelper.getFlowParams(), + mHelper.getApplicationContext(), + mHelper.getFlowParams(), email); - mActivityHelper.startActivityForResult(signInIntent, RC_SIGN_IN); + mHelper.startActivityForResult(signInIntent, RC_SIGN_IN); return; } Intent intent = WelcomeBackIdpPrompt.createIntent( - mActivityHelper.getApplicationContext(), - mActivityHelper.getFlowParams(), + mHelper.getApplicationContext(), + mHelper.getFlowParams(), provider, null, email); - mActivityHelper.startActivityForResult(intent, RC_WELCOME_BACK_IDP); + mHelper.startActivityForResult(intent, RC_WELCOME_BACK_IDP); return; } Intent signInIntent = new Intent( - mActivityHelper.getApplicationContext(), SignInActivity.class); + mHelper.getApplicationContext(), SignInActivity.class); signInIntent.putExtra(ExtraConstants.EXTRA_EMAIL, email); - mActivityHelper.startActivityForResult(signInIntent, RC_SIGN_IN); + mHelper.startActivityForResult(signInIntent, RC_SIGN_IN); } } public void onActivityResult(int requestCode, int resultCode, Intent data) { if (REQUEST_CODES.contains(requestCode)) { - mActivityHelper.finish(resultCode, data); + mHelper.finish(resultCode, data); } } } 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 b85c06bb5..000000000 --- a/auth/src/main/java/com/firebase/ui/auth/ui/ActivityBase.java +++ /dev/null @@ -1,35 +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 667c16613..ca4f3ba93 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 @@ -15,54 +15,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.StringRes; -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 com.firebase.ui.auth.util.BaseHelper; -import static com.firebase.ui.auth.util.Preconditions.checkNotNull; - -public class ActivityHelper { - private ProgressDialog mProgressDialog; +public class ActivityHelper extends BaseHelper { private Activity mActivity; - private final FlowParameters mFlowParams; public ActivityHelper(Activity 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)); } + @Override public void startActivityForResult(Intent intent, int requestCode) { mActivity.startActivityForResult(intent, requestCode); } @@ -71,39 +36,4 @@ 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")); - } } 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..52a2d98ef --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/ui/BaseFragment.java @@ -0,0 +1,23 @@ +package com.firebase.ui.auth.ui; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; + +import com.firebase.ui.auth.util.BaseHelper; + +/** + * A simple Fragment with {@code setRetainInstance} set to true. + */ +public class BaseFragment extends Fragment { + protected BaseHelper mHelper; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + mHelper = new FragmentHelper(this, + (FlowParameters) getArguments() + .getParcelable(ExtraConstants.EXTRA_FLOW_PARAMS)); + } +} 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 98d46f8a3..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/FragmentHelper.java b/auth/src/main/java/com/firebase/ui/auth/ui/FragmentHelper.java new file mode 100644 index 000000000..1d71e2b55 --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/ui/FragmentHelper.java @@ -0,0 +1,28 @@ +package com.firebase.ui.auth.ui; + +import android.content.Intent; +import android.support.v4.app.Fragment; + +import com.firebase.ui.auth.util.BaseHelper; +import com.firebase.ui.auth.util.smartlock.SignInDelegate; + +public class FragmentHelper extends BaseHelper { + private Fragment mFragment; + + public FragmentHelper(Fragment fragment, FlowParameters parameters) { + super(fragment.getContext(), parameters); + mFragment = fragment; + } + + @Override + public void startActivityForResult(Intent intent, int requestCode) { + mFragment.startActivityForResult(intent, requestCode); + } + + @Override + public void finish(int resultCode, Intent intent) { + if (mFragment instanceof SignInDelegate) { + ((SignInDelegate) mFragment).finish(resultCode, intent); + } + } +} 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 b3179d066..4e91bf220 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 @@ -38,7 +38,7 @@ 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.smartlock.SaveSmartLock; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.firebase.auth.AuthCredential; @@ -80,7 +80,8 @@ 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); + ((TextInputLayout) findViewById(R.id.password_layout)).setPasswordVisibilityToggleEnabled( + false); // Click listeners findViewById(R.id.button_done).setOnClickListener(this); @@ -133,20 +134,21 @@ public void onSuccess(AuthResult authResult) { // Sign in with the credential firebaseAuth.signInWithCredential(authCredential) .addOnFailureListener( - new TaskFailureLogger(TAG, "Error signing in with credential")) + new TaskFailureLogger(TAG, + "Error signing in with credential")) .addOnSuccessListener( new OnSuccessListener() { @Override public void onSuccess(AuthResult authResult) { - SmartLock + SaveSmartLock .getInstance(WelcomeBackPasswordPrompt.this, + mActivityHelper.getFlowParams(), TAG) - .saveCredentialsOrFinish( - WelcomeBackPasswordPrompt.this, - mActivityHelper, - authResult.getUser(), - password, - null /* provider */); + .saveCredentialsOrFinish(WelcomeBackPasswordPrompt.this, + mActivityHelper.getFlowParams(), + authResult.getUser(), + password, + null /* provider */); } }); } 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 deleted file mode 100644 index 94b95344c..000000000 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailHintContainerActivity.java +++ /dev/null @@ -1,83 +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.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.IntentSender; -import android.os.Bundle; - -import com.firebase.ui.auth.ui.AcquireEmailHelper; -import com.firebase.ui.auth.ui.ActivityHelper; -import com.firebase.ui.auth.ui.AppCompatBase; -import com.firebase.ui.auth.ui.FlowParameters; -import com.firebase.ui.auth.util.FirebaseAuthWrapper; -import com.firebase.ui.auth.util.FirebaseAuthWrapperFactory; -import com.google.android.gms.auth.api.credentials.Credential; - -public class EmailHintContainerActivity extends AppCompatBase { - private static final int RC_HINT = 13; - private AcquireEmailHelper mAcquireEmailHelper; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mAcquireEmailHelper = new AcquireEmailHelper(mActivityHelper); - FirebaseAuthWrapper apiWrapper = - FirebaseAuthWrapperFactory.getFirebaseAuthWrapper(mActivityHelper.getAppName()); - - PendingIntent hintIntent = apiWrapper.getEmailHintIntent(this); - if (hintIntent != null) { - try { - startIntentSenderForResult(hintIntent.getIntentSender(), RC_HINT, null, 0, 0, 0); - return; - } catch (IntentSender.SendIntentException e) { - e.printStackTrace(); - } - } - finish(RESULT_CANCELED, new Intent()); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == RC_HINT && data != null) { - Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY); - if (credential == null) { - // If the hint picker is cancelled show the SignInNoPasswordActivity - startActivityForResult( - SignInNoPasswordActivity.createIntent( - this, - mActivityHelper.getFlowParams(), - null), - AcquireEmailHelper.RC_SIGN_IN); - return; - } - mAcquireEmailHelper.checkAccountExists(credential.getId()); - } else { - mAcquireEmailHelper.onActivityResult(requestCode, resultCode, data); - } - } - - public static Intent createIntent( - Context context, - FlowParameters flowParams) { - return ActivityHelper.createBaseIntent( - context, - EmailHintContainerActivity.class, - flowParams); - } -} 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 0974c1c91..5c056eee3 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 @@ -39,7 +39,7 @@ 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.smartlock.SaveSmartLock; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; @@ -154,9 +154,11 @@ public void onComplete(@NonNull Task task) { // This executes even if the name change fails, since // the account creation succeeded and we want to save // the credential to SmartLock (if enabled). - SmartLock.getInstance(RegisterEmailActivity.this, TAG) + SaveSmartLock.getInstance(RegisterEmailActivity.this, + mActivityHelper.getFlowParams(), + TAG) .saveCredentialsOrFinish(RegisterEmailActivity.this, - mActivityHelper, + mActivityHelper.getFlowParams(), firebaseUser, password, null /* provider */); 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 7d091eac7..140aebc8f 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 @@ -34,7 +34,7 @@ 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.smartlock.SaveSmartLock; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.firebase.auth.AuthResult; @@ -98,9 +98,11 @@ private void signIn(String email, final String password) { @Override public void onSuccess(AuthResult authResult) { // Save credential in SmartLock (if enabled) - SmartLock.getInstance(SignInActivity.this, TAG) + SaveSmartLock.getInstance(SignInActivity.this, + mActivityHelper.getFlowParams(), + TAG) .saveCredentialsOrFinish(SignInActivity.this, - mActivityHelper, + mActivityHelper.getFlowParams(), authResult.getUser(), password, null /* provider */); 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 ca746e5e5..7df9d9ae8 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 @@ -25,19 +25,20 @@ import com.firebase.ui.auth.AuthUI; import com.firebase.ui.auth.AuthUI.IdpConfig; import com.firebase.ui.auth.BuildConfig; +import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.R; 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.IdpResponse; 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.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.EmailHintContainer; +import com.firebase.ui.auth.util.smartlock.SaveSmartLock; import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.FacebookAuthProvider; import com.google.firebase.auth.FirebaseAuth; @@ -51,18 +52,20 @@ * Presents the list of authentication options for this app to the user. If an * identity provider option is selected, a {@link IdpSignInContainerActivity container activity} * is launched to manage the IDP-specific sign-in flow. If email authentication is chosen, - * the {@link EmailHintContainerActivity root email flow activity} is started. - * + * the {@link EmailHintContainer root email flow activity} is started. + *

*

* Authentication picker activity rendered with all authentication options and default settings + * src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWgAAAKACAMAAACGxBKVAAAAOVBMVEXu7u7Nzs7h4eLFwsMvPp7QAho7WZg+ULQAAAD///8yUpQ2SbNpfLxteG+eqL6Rk5Hrj4zfQ1BERESoYitxAAALMElEQVR42uzdDXuiOBSG4XwoXqEhwP//s3tOAkhrrO7u6G7wuWtLichcfTkeIjN1zAlvYYiAoAkaBP1WMcV0Ieh/4RKLy9f3Ufm4Bnu52BDCs0kT9P2Ys7V45fZt4HTSmFUi6H/YEL67LOGv+e+DdiKsIykEUzaOEn/cFgT9S9LJGYnR70t6TXoXtIZn1ixjiBfj8rfBS1fRxZcuCPpu60hm6QrBxl36Jeld0D6XdVwLOn9Gd4nhcrpI7HKPfkvQd6Wwuewbd/lyDVqCNFvr+Ao+Sr+ORoI+adAn46JzVPRv3E3OpXNr0rugU9SDEncnx0tpIiVoevQTc4SSc/w5FdGT4u5kaLX4d62jtOQlaFrHM2065+xv5yKnyz7oy65orSkRn3K6Ue/7yoVN0L/OPCRBV5tcn3ZBu7gre2nQ0Rg9GZ7kDmckZBuZdTxzQqzPrndB22/9JZpgL3oyPF2cfieZS+IE/Yj98dr6soi8MnyH/jZ5rnX8v+YxREDQBI1/EPQFb0HQBE3QIGiCJmgiIGiCBkETNEGDoAkaBE3QBA2CJmgQNEETNAiaoEHQBE3QIGiCBkETNEGDoAkaBE3QBA2CJmgQNEETNAiaoEHQBE3QIOhjBP2FtzDWeryeNZIztxff5EOCtrrg9rpb+aJB4w2Mw2vZsiDoNzHOGMfHCz+MWRcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtcsY1dGs359CYRqN2oTkUNCV9rIomaIImaIImaIImaIImaIImaIIm6NcGbZw1BP3yoO2cTQT92qCnef7/J32AoHPOk379vXu4J8e2Ue+fitD6zwjaraV8t0uPwzD4YId0m1FlrIw6G8IwbENJdpHu7v4zgp4etYxxSHYcbPCVA+GrB0dGNb1hvOY8yi7Shwc95ZZhiztBmNGb0cpi9PqdH8ecmoxdV9Io6xJxklGp4FEqOo0l61zb+r1s6/OmeSEPlAfp/u2yj2MHnQs6FrX61MYhP6l81eiGZPLClrHrih/kJuteRr3Wr4RdytistZ10xK+LURc5aDeMaUifHrQmrV1XE0wSmgSd19ag1xU7OMnNDc7lEEsd54glR6llrfExj+4XOehxMI86yCFah646cSdouVNqVuKzkuD9oMOQBi+fYQt6XILWinZ+iVZCLYtvQaujBz2sT2wbY+3pq3dLLT+saI1L20W4qejSo3+U8s+KDubgQUuTHUb9Ib0UtKueDMdUKnrr0fWgfU7NL0Gna0VLV9EHDqU5p3Vx7dFW/4jx6C9Y/LDoYv18pO1VZxz6Ka1BZhUS5phPhnnWsazIwsn0weUtndapdoi09h6ZjWzTjXUhext1tiIPKwf72C/Bt6THhy/inITj/9SraqOvgsbPeQmu0+Ecs334047PHI2n5QNsPinoHPZzRfhHr6Ya9/yFcS78c/WOoAmaoAmaoAmaoAmaoAmaoAmaoAn6Ltte0LbNiraN/QKcsa3+Rqf1TWk1Z+OcbYpr+LfBAQAAAAAADsPGvinRNhp015xGC7pvLug2S9o3F3TvCZqgCfo/lwiaiiZogiZoejQVTdAETdAE/a96dLzqCfplFd3v/wVvIujXBb3+m3SfUjXoPt+6WLkrViMpozGt95YLytUt07ODBwo6Spy1oKMJwXW9Cbc/emVsGY2xux61tOz/dlNbGa0OHqFHr0H3nTO1oIORSjd91EdIXeb6XupTx7aVfq1/GY2yIwl6uScFm1LKlV0KvFsXPnQ3YzrYVZ4Ah6loqedaj46aV7Kdd7IwLrkuOVnG/KO7fl3po4u9T50svOtcPjrJmViCzotodUt5hNE/xeuKD/JovcsZvy28bO7sUYPWhhqrPVrf0qrXrXopbDlxdlYXps+P7LeVLvg+6LqMyiYuRX1/mVCCTjKh0adG0J04K8fO6Js9Sqb50VHH3LqQoE2tpRwjaJ3Y9fWToYRrooYqCUqQsp4jXoPeVlyU3Rinq6V1xKWWy1s+6ixStpZ7e+tj8Ppc8WWP7ttC0q829GP0aAmoM9UeLe1SK1ECMprRvaC1bp3TxrwFnbagpaL1gMlptZOdyC7j+ma8ubHo2HXh75w5D96jtWNqLT+qaCnQIH05xFrQcWn2vlR0SlrR5by3jm0LCVp61OcFLaNe+2ju0c6Hu0HLhhp2X4I2Kd4E7fQ45B7t5dlhc6ayx9yjfenRPvdoecTtJYLDB63/csXmGYbOOqSqZeKha+usY1mRhZWQfZ+39MbL/EM2zRGXOUqes0RZ5BmG1/a1zFl0zHbrQndpXTzetY60C7rWG8v0N097tWzLVHqb/fbXSe/y7bp5/3M6XMa+zZn7detlf/11P/3hrt7160vkO6+Td1Pq+qHgWscfv0waIxeVuB7N3xlS0QRN0ARN0PRoKpqgCZqg6dEE/bdYgn4P1zWn0Xdy9F1Tv8/Zdb7RoG1zTJt437v3RS1ht/JpeHtBAAAAAADwKzufmzK3evXu3JxGC7q9oBt937v2gm7z7wyn9oKeqGgqmoqmoqloKpqKpqKpaCqaiqaiqWgquvWKnsW0fFLRr6voef/fQv9W6vOTY9vo9PCJIxtO86dU9GTm9T8Jn6bq9VOfj8BcOQpz9cjoqD45wqPL3lOY5zB9SkXLTyufwp3n6oXqKUyz041q99UDPJ9d0Gv1jwp6Oj8d9AEqOkfsSty1oE3QSCTsWS+/T246u8mXY6Vj24o8HXTd6paTkZ0GM61HbjJyz+Rmp3fZ/FdTdtbNP6ii56U35wZSr+i8vUYiG5p1Me/H5rydFv9Z6l96UNnM2pKj0W3kXjctY7LLEP5W6zhEResbspV33bX1Vrr06JxKznJ5ys/fV+R54eYSnrYO7dF5j3PZZgrl6SFjMsHxYf60Hv141qGdZQl6vhu0fJc/zmvQ5hp0OdVeg87vgveRFb2oVbQWZkn594qWcMM5N/xaRZfesgStA9OnVfT8KGhtraZEImXp7gc9BX+2y2ppxEvQ2qNzrteg3ef16McvWCanc4Y863BGz3cy8chry6xjWcmL66c5WwnZTWtMUsMuT010TGYhMgeZ841rHZVePeeJBdc6Xn31zubXNVzreMPVu//suhPXo6lorkdT0VQ0FU1FU9FUNBVNRVPRVDQVTUVT0Xe59oJu9X3vmito3veO9707VNINv+9dUwwAAAAA4K/27ljZQRAIo/ACjbdYmNn3f9iLUSNVMil+B8fzTYqUeiQEKBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+OIPl7AFlyA0oQkNQhOa0CQgNKFBaEITGoQm9GeeXHnJ4R6E7h2K5aS74Lb9FH8TPsjxx/7TtKFbjyAMnS15hPeHGbpHWUo9XhZzhvZsJZaUdJ33Wckty0IPTWLO0JEsrRcmG9HN/ExQHxu62N5BNqKtnO+rxTND98n5GGOqEe3mYwN/ZOicy/uakiz0eNuqlcfcocPyed+qEd0I/foizH7liH7q1HEs7ZQjehlXGu2pX4b7ZkW66hj2D2G6NeT0odcS6/whG9FxbgiTakDfI/S2Z9HtDKN/ZvqtR8smO7hqub5NuwXfpmrhWUfsf1eadAd4NzlUeu3Z6qITrdZ2v3NSDv4JTWgQmtCEJgGhCQ1CE5rQIDSh8bN/7xna+k/jxQQAAAAASUVORK5CYII="> */ -public class AuthMethodPickerActivity extends IDPBaseActivity +public class AuthMethodPickerActivity extends AppCompatBase implements IdpCallback, View.OnClickListener { 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; + private EmailHintContainer mEmailHintContainer; @Override protected void onCreate(Bundle savedInstanceState) { @@ -149,7 +152,11 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } else if (requestCode == RC_ACCOUNT_LINK) { finish(resultCode, data); } else { - for(IdpProvider provider : mIdpProviders) { + if (mEmailHintContainer != null) { + mEmailHintContainer.onActivityResult(this, requestCode, resultCode, data); + } + + for (IdpProvider provider : mIdpProviders) { provider.onActivityResult(requestCode, resultCode, data); } } @@ -157,7 +164,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { @Override public void onSuccess(final IdpResponse response) { - AuthCredential credential = createCredential(response); + AuthCredential credential = IdpResponse.createCredential(response); final FirebaseAuth firebaseAuth = mActivityHelper.getFirebaseAuth(); firebaseAuth @@ -167,7 +174,9 @@ public void onSuccess(final IdpResponse response) { .addOnCompleteListener(new CredentialSignInHandler( AuthMethodPickerActivity.this, mActivityHelper, - SmartLock.getInstance(AuthMethodPickerActivity.this, TAG), + SaveSmartLock.getInstance(AuthMethodPickerActivity.this, + mActivityHelper.getFlowParams(), + TAG), RC_ACCOUNT_LINK, response)); } @@ -181,10 +190,10 @@ public void onFailure(Bundle extra) { @Override public void onClick(View view) { if (view.getId() == R.id.email_provider) { - Intent intent = EmailFlowUtil.createIntent( - this, - mActivityHelper.getFlowParams()); - startActivityForResult(intent, RC_EMAIL_FLOW); + mEmailHintContainer = EmailFlowUtil.startEmailFlow(this, + null, + mActivityHelper, + RC_EMAIL_FLOW); } } 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 e5a8da173..5fac8c4f8 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 @@ -23,7 +23,8 @@ 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.BaseHelper; +import com.firebase.ui.auth.util.smartlock.SaveSmartLock; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; @@ -39,19 +40,19 @@ public class CredentialSignInHandler implements OnCompleteListener { private final static String TAG = "CredentialSignInHandler"; private AppCompatBase mActivity; - private ActivityHelper mActivityHelper; - private SmartLock mSmartLock; + private BaseHelper mHelper; + private SaveSmartLock mSmartLock; private IdpResponse mResponse; private int mAccountLinkResultCode; public CredentialSignInHandler( AppCompatBase activity, - ActivityHelper activityHelper, - SmartLock smartLock, + ActivityHelper helper, + SaveSmartLock smartLock, int accountLinkResultCode, IdpResponse response) { mActivity = activity; - mActivityHelper = activityHelper; + mHelper = helper; mSmartLock = smartLock; mResponse = response; mAccountLinkResultCode = accountLinkResultCode; @@ -62,14 +63,14 @@ public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { FirebaseUser firebaseUser = task.getResult().getUser(); mSmartLock.saveCredentialsOrFinish(mActivity, - mActivityHelper, + mHelper.getFlowParams(), firebaseUser, null /* password */, mResponse.getProviderType()); } 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")) @@ -83,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()); } @@ -99,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/ui/idp/IDPBaseActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/IDPBaseActivity.java deleted file mode 100644 index fee155e1d..000000000 --- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/IDPBaseActivity.java +++ /dev/null @@ -1,41 +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.idp; - -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.firebase.ui.auth.ui.AppCompatBase; -import com.google.firebase.auth.AuthCredential; -import com.google.firebase.auth.FacebookAuthProvider; -import com.google.firebase.auth.GoogleAuthProvider; -import com.google.firebase.auth.TwitterAuthProvider; - -public class IDPBaseActivity extends AppCompatBase { - protected AuthCredential createCredential(IdpResponse idpSignInResponse) { - if (idpSignInResponse.getProviderType().equalsIgnoreCase(FacebookAuthProvider.PROVIDER_ID)) { - return FacebookProvider.createAuthCredential(idpSignInResponse); - } else if (idpSignInResponse.getProviderType().equalsIgnoreCase(GoogleAuthProvider - .PROVIDER_ID)) { - return GoogleProvider.createAuthCredential(idpSignInResponse); - } else if (idpSignInResponse - .getProviderType() - .equalsIgnoreCase(TwitterAuthProvider.PROVIDER_ID)) { - return TwitterProvider.createAuthCredential(idpSignInResponse); - } - return null; - } -} diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/IdpSignInContainerActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/IdpSignInContainerActivity.java index a6c5bffa6..0e5697a30 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/IdpSignInContainerActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/idp/IdpSignInContainerActivity.java @@ -17,6 +17,7 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; + import com.firebase.ui.auth.AuthUI.IdpConfig; import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.provider.FacebookProvider; @@ -25,19 +26,20 @@ 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.ExtraConstants; import com.firebase.ui.auth.ui.FlowParameters; import com.firebase.ui.auth.ui.TaskFailureLogger; -import com.firebase.ui.auth.util.SmartLock; +import com.firebase.ui.auth.util.smartlock.SaveSmartLock; import com.google.android.gms.tasks.Task; import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.AuthResult; import com.google.firebase.auth.FacebookAuthProvider; import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.auth.TwitterAuthProvider; import com.google.firebase.auth.GoogleAuthProvider; +import com.google.firebase.auth.TwitterAuthProvider; -public class IdpSignInContainerActivity extends IDPBaseActivity implements IdpCallback { +public class IdpSignInContainerActivity extends AppCompatBase implements IdpCallback { private static final String TAG = "IDPSignInContainer"; private static final int RC_WELCOME_BACK_IDP = 4; @@ -78,7 +80,7 @@ protected void onCreate(Bundle savedInstanceState) { public void onSuccess(final IdpResponse response) { Intent data = new Intent(); data.putExtra(ExtraConstants.EXTRA_IDP_RESPONSE, response); - AuthCredential credential = createCredential(response); + AuthCredential credential = IdpResponse.createCredential(response); final FirebaseAuth firebaseAuth = mActivityHelper.getFirebaseAuth(); Task authResultTask = firebaseAuth.signInWithCredential(credential); authResultTask @@ -87,7 +89,9 @@ public void onSuccess(final IdpResponse response) { .addOnCompleteListener(new CredentialSignInHandler( IdpSignInContainerActivity.this, mActivityHelper, - SmartLock.getInstance(IdpSignInContainerActivity.this, TAG), + SaveSmartLock.getInstance(IdpSignInContainerActivity.this, + mActivityHelper.getFlowParams(), + TAG), RC_WELCOME_BACK_IDP, response)); } diff --git a/auth/src/main/java/com/firebase/ui/auth/util/BaseHelper.java b/auth/src/main/java/com/firebase/ui/auth/util/BaseHelper.java new file mode 100644 index 000000000..3683ed7d1 --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/util/BaseHelper.java @@ -0,0 +1,92 @@ +package com.firebase.ui.auth.util; + +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.StringRes; + +import com.firebase.ui.auth.ui.ExtraConstants; +import com.firebase.ui.auth.ui.FlowParameters; +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 com.firebase.ui.auth.util.Preconditions.checkNotNull; + +public abstract class BaseHelper { + private ProgressDialog mProgressDialog; + private Context mContext; + private final FlowParameters mFlowParams; + + public BaseHelper(Context context, FlowParameters parameters) { + mContext = context; + mFlowParams = parameters; + } + + public void configureTheme() { + mContext.setTheme(mFlowParams.themeId); + } + + public FlowParameters getFlowParams() { + return mFlowParams; + } + + 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.isShowing()) { + mProgressDialog.dismiss(); + mProgressDialog = null; + } + } + + public abstract void startActivityForResult(Intent intent, int requestCode); + + public abstract void finish(int resultCode, Intent intent); + + 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")); + } +} 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 7f514c958..000000000 --- a/auth/src/main/java/com/firebase/ui/auth/util/CredentialsAPI.java +++ /dev/null @@ -1,267 +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); - } - - 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/EmailFlowUtil.java b/auth/src/main/java/com/firebase/ui/auth/util/EmailFlowUtil.java index d0db5c591..71e248d49 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/EmailFlowUtil.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/EmailFlowUtil.java @@ -1,27 +1,40 @@ package com.firebase.ui.auth.util; -import android.content.Context; -import android.content.Intent; +import android.support.annotation.Nullable; +import android.support.v4.app.FragmentActivity; -import com.firebase.ui.auth.ui.FlowParameters; -import com.firebase.ui.auth.ui.email.EmailHintContainerActivity; import com.firebase.ui.auth.ui.email.SignInNoPasswordActivity; +import com.firebase.ui.auth.util.smartlock.SignInDelegate; /** * Helper class to kick off the Email/Password sign-in flow. */ public class EmailFlowUtil { - /** - * Return an intent for either {@link EmailHintContainerActivity} or + * Return an intent for either {@link EmailHintContainer} or * {@link SignInNoPasswordActivity} depending on if SmartLock is enabled. */ - public static Intent createIntent(Context context, FlowParameters parameters) { - if (parameters.smartLockEnabled) { - return EmailHintContainerActivity.createIntent(context, parameters); + public static EmailHintContainer startEmailFlow(FragmentActivity activity, + @Nullable SignInDelegate signInDelegate, + BaseHelper helper, + int requestCode) { + if (helper.getFlowParams().smartLockEnabled) { + if (signInDelegate != null) { + return new EmailHintContainer(helper).trySignInWithEmailAndPassword(signInDelegate); + } else { + return new EmailHintContainer(helper).trySignInWithEmailAndPassword(activity); + } } else { - return SignInNoPasswordActivity.createIntent(context, parameters, null); + if (signInDelegate != null) { + signInDelegate.startActivityForResult( + SignInNoPasswordActivity.createIntent(signInDelegate.getContext(), helper.getFlowParams(), null), + requestCode); + } else { + activity.startActivityForResult( + SignInNoPasswordActivity.createIntent(activity, helper.getFlowParams(), null), + requestCode); + } } + return null; } - } diff --git a/auth/src/main/java/com/firebase/ui/auth/util/EmailHintContainer.java b/auth/src/main/java/com/firebase/ui/auth/util/EmailHintContainer.java new file mode 100644 index 000000000..121703f02 --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/util/EmailHintContainer.java @@ -0,0 +1,123 @@ +/* + * 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.PendingIntent; +import android.content.Intent; +import android.content.IntentSender; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.util.Log; + +import com.firebase.ui.auth.ui.AcquireEmailHelper; +import com.firebase.ui.auth.ui.email.SignInNoPasswordActivity; +import com.google.android.gms.auth.api.credentials.Credential; + +public class EmailHintContainer { + private static final int RC_HINT = 13; + private AcquireEmailHelper mAcquireEmailHelper; + private BaseHelper mHelper; + + public EmailHintContainer(BaseHelper helper) { + mHelper = helper; + } + + public void onActivityResult(Fragment fragment, int requestCode, int resultCode, Intent data) { + if (requestCode == RC_HINT && data != null) { + Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY); + if (credential == null) { + // If the hint picker is cancelled show the SignInNoPasswordActivity + fragment.startActivityForResult( + SignInNoPasswordActivity.createIntent( + fragment.getContext(), + mHelper.getFlowParams(), + null), + AcquireEmailHelper.RC_SIGN_IN); + return; + } + mAcquireEmailHelper.checkAccountExists(credential.getId()); + } else { + mAcquireEmailHelper.onActivityResult(requestCode, resultCode, data); + } + } + + public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { + if (requestCode == RC_HINT && data != null) { + Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY); + if (credential == null) { + // If the hint picker is cancelled show the SignInNoPasswordActivity + activity.startActivityForResult( + SignInNoPasswordActivity.createIntent( + activity, + mHelper.getFlowParams(), + null), + AcquireEmailHelper.RC_SIGN_IN); + return; + } + mAcquireEmailHelper.checkAccountExists(credential.getId()); + } else { + mAcquireEmailHelper.onActivityResult(requestCode, resultCode, data); + } + } + + public EmailHintContainer trySignInWithEmailAndPassword(Fragment fragment) { + mAcquireEmailHelper = new AcquireEmailHelper(mHelper); + FirebaseAuthWrapper apiWrapper = + FirebaseAuthWrapperFactory.getFirebaseAuthWrapper(mHelper.getAppName()); + + PendingIntent hintIntent = apiWrapper.getEmailHintIntent(fragment.getActivity()); + if (hintIntent != null) { + try { + fragment.startIntentSenderForResult(hintIntent.getIntentSender(), + RC_HINT, + null, + 0, + 0, + 0, + null); + return this; + } catch (IntentSender.SendIntentException e) { + Log.e("EmailHintContainer", "Failed to send Credentials intent.", e); + } + } + mHelper.finish(Activity.RESULT_CANCELED, new Intent()); + return null; + } + + public EmailHintContainer trySignInWithEmailAndPassword(FragmentActivity activity) { + mAcquireEmailHelper = new AcquireEmailHelper(mHelper); + FirebaseAuthWrapper apiWrapper = + FirebaseAuthWrapperFactory.getFirebaseAuthWrapper(mHelper.getAppName()); + + PendingIntent hintIntent = apiWrapper.getEmailHintIntent(activity); + if (hintIntent != null) { + try { + activity.startIntentSenderForResult(hintIntent.getIntentSender(), + RC_HINT, + null, + 0, + 0, + 0, + null); + return this; + } catch (IntentSender.SendIntentException e) { + Log.e("EmailHintContainer", "Failed to send Credentials intent.", e); + } + } + mHelper.finish(Activity.RESULT_CANCELED, new Intent()); + return null; + } +} 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 df8e06953..b7cb26178 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 @@ -14,7 +14,6 @@ package com.firebase.ui.auth.util; -import android.app.Fragment; import android.app.PendingIntent; import android.content.Context; import android.os.Bundle; diff --git a/auth/src/main/java/com/firebase/ui/auth/util/SmartLock.java b/auth/src/main/java/com/firebase/ui/auth/util/smartlock/SaveSmartLock.java similarity index 68% rename from auth/src/main/java/com/firebase/ui/auth/util/SmartLock.java rename to auth/src/main/java/com/firebase/ui/auth/util/smartlock/SaveSmartLock.java index 864e2928f..2ef8e8a5a 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/SmartLock.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/smartlock/SaveSmartLock.java @@ -12,9 +12,10 @@ * limitations under the License. */ -package com.firebase.ui.auth.util; +package com.firebase.ui.auth.util.smartlock; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.net.Uri; @@ -22,50 +23,46 @@ 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.text.TextUtils; import android.util.Log; +import android.widget.Toast; import com.firebase.ui.auth.BuildConfig; -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.FirebaseAuthWrapperFactory; +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.IdentityProviders; 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.ResultCallback; import com.google.android.gms.common.api.Status; -import com.google.firebase.auth.FacebookAuthProvider; import com.google.firebase.auth.FirebaseUser; -import com.google.firebase.auth.GoogleAuthProvider; -import com.google.firebase.auth.TwitterAuthProvider; import static android.app.Activity.RESULT_CANCELED; import static android.app.Activity.RESULT_FIRST_USER; import static android.app.Activity.RESULT_OK; -public class SmartLock extends Fragment - implements GoogleApiClient.ConnectionCallbacks, - GoogleApiClient.OnConnectionFailedListener, - ResultCallback { - private static final String TAG = "CredentialsSaveBase"; +public class SaveSmartLock extends SmartLock { + private static final String TAG = "SaveSmartLock"; private static final int RC_SAVE = 100; private static final int RC_UPDATE_SERVICE = 28; - private AppCompatBase mActivity; - private ActivityHelper mActivityHelper; + private GoogleApiClient mGoogleApiClient; private String mName; private String mEmail; private String mPassword; private String mProvider; private String mProfilePictureUri; - 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(RESULT_CANCELED); return; @@ -76,25 +73,15 @@ public void onConnected(@Nullable Bundle bundle) { if (mPassword == null) { // only password OR provider can be set, not both if (mProvider != null) { - String translatedProvider = null; - // translate the google.com/facebook.com provider strings into full URIs - switch (mProvider) { - case GoogleAuthProvider.PROVIDER_ID: - translatedProvider = IdentityProviders.GOOGLE; - break; - case FacebookAuthProvider.PROVIDER_ID: - translatedProvider = IdentityProviders.FACEBOOK; - break; - case TwitterAuthProvider.PROVIDER_ID: - translatedProvider = IdentityProviders.TWITTER; - break; - default: - Log.e(TAG, "Unable to save null credential!"); - finish(RESULT_CANCELED); - return; - } + String translatedProvider = SmartLock.providerIdToAccountType(mProvider); - builder.setAccountType(translatedProvider); + if (translatedProvider != null) { + builder.setAccountType(translatedProvider); + } else { + Log.e(TAG, "Unable to save null credential!"); + finish(RESULT_CANCELED); + return; + } } } @@ -106,8 +93,8 @@ 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); } @@ -124,10 +111,12 @@ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { 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() - .getErrorResolutionPendingIntent(mActivity, + .getErrorResolutionPendingIntent(getActivity(), connectionResult.getErrorCode(), RC_UPDATE_SERVICE); try { @@ -139,12 +128,11 @@ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { 0, null); } catch (IntentSender.SendIntentException e) { - e.printStackTrace(); + Log.e(TAG, "STATUS: Failed to send resolution.", e); finish(RESULT_CANCELED); } } - @Override public void onResult(@NonNull Status status) { if (status.isSuccess()) { @@ -190,8 +178,8 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { 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"); @@ -201,25 +189,33 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } private void finish(int resultCode) { - mActivity.finish(RESULT_OK, mActivity.getIntent()); + if (mGoogleApiClient != null) { + mGoogleApiClient.disconnect(); + } + ((AppCompatBase) getActivity()).finish(RESULT_OK, getActivity().getIntent()); } /** * 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. * @param firebaseUser Firebase user to save in Credential. * @param password (optional) password for email credential. * @param provider (optional) provider string for provider credential. */ - public void saveCredentialsOrFinish(AppCompatBase activity, - ActivityHelper helper, + public void saveCredentialsOrFinish(Context context, + FlowParameters parameters, FirebaseUser firebaseUser, @Nullable String password, @Nullable String provider) { - mActivity = activity; - mActivityHelper = helper; + if (!parameters.smartLockEnabled + || !PlayServicesHelper.getInstance(getActivity()).isPlayServicesAvailable() + || !FirebaseAuthWrapperFactory.getFirebaseAuthWrapper(parameters.appName) + .isPlayServicesAvailable(getActivity())) { + finish(RESULT_CANCELED); + return; + } + mName = firebaseUser.getDisplayName(); mEmail = firebaseUser.getEmail(); mPassword = password; @@ -227,45 +223,33 @@ public void saveCredentialsOrFinish(AppCompatBase activity, mProfilePictureUri = firebaseUser.getPhotoUrl() != null ? firebaseUser.getPhotoUrl() .toString() : null; - // If SmartLock is disabled, finish the Activity - if (!helper.getFlowParams().smartLockEnabled) { - finish(RESULT_CANCELED); - return; - } - - // If Play Services is not available, finish the Activity - if (!PlayServicesHelper.getInstance(activity).isPlayServicesAvailable()) { - finish(RESULT_CANCELED); - return; - } - - if (!FirebaseAuthWrapperFactory - .getFirebaseAuthWrapper(helper.getFlowParams().appName) - .isPlayServicesAvailable(activity)) { - finish(RESULT_CANCELED); - return; - } - - mCredentialsApiClient = new GoogleApiClient.Builder(activity) + mGoogleApiClient = new GoogleApiClient.Builder(context) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(Auth.CREDENTIALS_API) - .enableAutoManage(activity, this) .build(); + mGoogleApiClient.connect(); } - public static SmartLock getInstance(AppCompatBase activity, String tag) { - SmartLock result; + public static SaveSmartLock getInstance(FragmentActivity activity, + FlowParameters parameters, + String tag) { + SaveSmartLock result; FragmentManager fm = activity.getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); Fragment fragment = fm.findFragmentByTag(tag); - if (fragment == null || !(fragment instanceof SmartLock)) { - result = new SmartLock(); + if (fragment == null || !(fragment instanceof SaveSmartLock)) { + result = new SaveSmartLock(); + + Bundle bundle = new Bundle(); + bundle.putParcelable(ExtraConstants.EXTRA_FLOW_PARAMS, parameters); + result.setArguments(bundle); + ft.add(result, tag).disallowAddToBackStack().commit(); } else { - result = (SmartLock) fragment; + result = (SaveSmartLock) fragment; } return result; diff --git a/auth/src/main/java/com/firebase/ui/auth/util/smartlock/SignInDelegate.java b/auth/src/main/java/com/firebase/ui/auth/util/smartlock/SignInDelegate.java new file mode 100644 index 000000000..e1d4ef0f6 --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/util/smartlock/SignInDelegate.java @@ -0,0 +1,434 @@ +package com.firebase.ui.auth.util.smartlock; + +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.support.v4.app.FragmentTransaction; +import android.text.TextUtils; +import android.util.Log; + +import com.firebase.ui.auth.AuthUI; +import com.firebase.ui.auth.BuildConfig; +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.TaskFailureLogger; +import com.firebase.ui.auth.ui.email.SignInNoPasswordActivity; +import com.firebase.ui.auth.ui.idp.AuthMethodPickerActivity; +import com.firebase.ui.auth.ui.idp.IdpSignInContainerActivity; +import com.firebase.ui.auth.util.CredentialsApiHelper; +import com.firebase.ui.auth.util.EmailFlowUtil; +import com.firebase.ui.auth.util.EmailHintContainer; +import com.firebase.ui.auth.util.FirebaseAuthWrapperFactory; +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.auth.api.signin.GoogleSignInOptions; +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.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 SmartLock { + 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 GoogleApiClient mGoogleApiClient; + private Credential mCredential; + private EmailHintContainer mEmailHintContainer; + + @Override + public 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 + 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() + || !FirebaseAuthWrapperFactory.getFirebaseAuthWrapper(mHelper.getAppName()) + .isPlayServicesAvailable(getActivity())) { + 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); + initGoogleApiClient(null); + mHelper.getCredentialsApi() + .request(mGoogleApiClient, + new CredentialRequest.Builder() + .setPasswordLoginSupported(true) + .setAccountTypes(IdentityProviders.GOOGLE, + IdentityProviders.FACEBOOK, + IdentityProviders.TWITTER) + .build()) + .setResultCallback(this); + } else { + startAuthMethodChoice(); + } + } + + @Override + public void onResult(@NonNull CredentialRequestResult result) { + Status status = result.getStatus(); + + if (status.isSuccess()) { + // Auto sign-in success + handleCredential(result.getCredential()); + String email = getEmailFromCredential(); + String password = getPasswordFromCredential(); + + 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); + } + } else if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) { + mHelper.dismissDialog(); // TODO: 10/22/2016 + // resolve saved emails + try { + startIntentSenderForResult(status.getResolution().getIntentSender(), + RC_CREDENTIALS_READ, + null, + 0, + 0, + 0, + null); + } catch (IntentSender.SendIntentException e) { + Log.e(TAG, "Failed to send Credentials intent.", e); + } + } + } + + @Override + public void onConnected(@Nullable Bundle bundle) { + } + + @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); + handleCredential(credential); + + String email = getEmailFromCredential(); + String password = getPasswordFromCredential(); + if (email != null) { + if (password == null || password.isEmpty()) { + // identifier/provider combination + redirectToIdpSignIn(email, getAccountTypeFromCredential()); + } else { + // email/password combination + signInWithEmailAndPassword(email, password); + } + } + } 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: + if (mEmailHintContainer != null) { + mEmailHintContainer.onActivityResult(this, requestCode, resultCode, data); + } + break; + } + } + + 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; + + if (IdentityProviders.GOOGLE.equals(credential.getAccountType())) { + // Google account, rebuild GoogleApiClient to set account name and then try + initGoogleApiClient(credential.getId()); + // Try silent sign-in with Google Sign In API + Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient); + } else { + // Email/password account + String status = String.format("Signed in as %s", credential.getId()); + Log.d(TAG, status); + } + } + + private void initGoogleApiClient(String accountName) { + GoogleSignInOptions.Builder gsoBuilder = new GoogleSignInOptions + .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestEmail(); + + if (accountName != null) { + gsoBuilder.setAccountName(accountName); + } + + if (mGoogleApiClient != null) { + mGoogleApiClient.disconnect(); + } + + mGoogleApiClient = new GoogleApiClient.Builder(getContext()) + .addConnectionCallbacks(this) + .addApi(Auth.CREDENTIALS_API) + .addApi(Auth.GOOGLE_SIGN_IN_API, gsoBuilder.build()) + .build(); + mGoogleApiClient.connect(); + } + + private void startAuthMethodChoice() { + List providers = mHelper.getFlowParams().providerInfo; + + // If the only provider is Email, immediately launch the email flow. Otherwise, launch + // the auth method picker screen. + if (providers.size() == 1) { + if (providers.get(0).getProviderId().equals(EmailAuthProvider.PROVIDER_ID)) { + mEmailHintContainer = EmailFlowUtil.startEmailFlow(getActivity(), + this, + mHelper, + RC_EMAIL_FLOW); + } else { + redirectToIdpSignIn(null, + SmartLock.providerIdToAccountType( + providers.get(0).getProviderId())); + } + } else { + startActivityForResult( + AuthMethodPickerActivity.createIntent( + getContext(), + mHelper.getFlowParams()), + RC_AUTH_METHOD_PICKER); + } + } + + /** + * 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) { + Intent nextIntent; + switch (accountType) { + case IdentityProviders.GOOGLE: + nextIntent = IdpSignInContainerActivity.createIntent( + getContext(), + mHelper.getFlowParams(), + GoogleAuthProvider.PROVIDER_ID, + email); + break; + case IdentityProviders.FACEBOOK: + nextIntent = IdpSignInContainerActivity.createIntent( + getContext(), + mHelper.getFlowParams(), + FacebookAuthProvider.PROVIDER_ID, + email); + break; + case IdentityProviders.TWITTER: + nextIntent = IdpSignInContainerActivity.createIntent( + getContext(), + mHelper.getFlowParams(), + TwitterAuthProvider.PROVIDER_ID, + email); + break; + default: + Log.w(TAG, "unknown provider: " + accountType); + nextIntent = AuthMethodPickerActivity.createIntent( + getContext(), + mHelper.getFlowParams()); + } + startActivityForResult(nextIntent, RC_IDP_SIGNIN); + } + + public void finish(int resultCode, Intent data) { + if (mGoogleApiClient != null) { + mGoogleApiClient.disconnect(); + } + mHelper.dismissDialog(); + + try { + if (resultCode == RESULT_OK) { + ((AuthUI.SignInResult) getActivity()).onSignInSuccessful(data); + } else { + ((AuthUI.SignInResult) getActivity()).onSignInFailed(resultCode); + } + } catch (ClassCastException e) { + Log.e(TAG, getActivity().toString() + + " must implement AuthUI.AuthUIResult to receive sign in results"); + } + + getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit(); + } + + /** + * 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 delegateSignIn(FragmentActivity activity, FlowParameters parameters) { + FragmentManager fm = activity.getSupportFragmentManager(); + FragmentTransaction ft = fm.beginTransaction(); + + Fragment fragment = fm.findFragmentByTag(TAG); + if (fragment == null || !(fragment instanceof SignInDelegate)) { + SignInDelegate result = new SignInDelegate(); + + Bundle bundle = new Bundle(); + bundle.putParcelable(ExtraConstants.EXTRA_FLOW_PARAMS, parameters); + result.setArguments(bundle); + + ft.add(result, TAG).disallowAddToBackStack().commit(); + } + } +} diff --git a/auth/src/main/java/com/firebase/ui/auth/util/SmartLockUtil.java b/auth/src/main/java/com/firebase/ui/auth/util/smartlock/SmartLock.java similarity index 69% rename from auth/src/main/java/com/firebase/ui/auth/util/SmartLockUtil.java rename to auth/src/main/java/com/firebase/ui/auth/util/smartlock/SmartLock.java index 4fd61de00..5316fce3f 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/SmartLockUtil.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/smartlock/SmartLock.java @@ -1,11 +1,18 @@ -package com.firebase.ui.auth.util; +package com.firebase.ui.auth.util.smartlock; import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; +import android.widget.Toast; +import com.firebase.ui.auth.BuildConfig; +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 +24,27 @@ 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 SmartLock extends BaseFragment implements + GoogleApiClient.ConnectionCallbacks, + ResultCallback, + GoogleApiClient.OnConnectionFailedListener { + private static final String TAG = "SmartLockBase"; + + @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(getContext(), "An error has occurred.", Toast.LENGTH_SHORT).show(); + } /** * Translate a Firebase Auth provider ID (such as {@link GoogleAuthProvider#PROVIDER_ID}) to diff --git a/auth/src/main/res/layout/delete_credential.xml b/auth/src/main/res/layout/delete_credential.xml deleted file mode 100644 index df77836c0..000000000 --- a/auth/src/main/res/layout/delete_credential.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - -