// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.chrome.browser.firstrun; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.CommandLine; import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.IntentHandler.ExternalAppId; import org.chromium.chrome.browser.preferences.PreferencesLauncher; import org.chromium.chrome.browser.signin.AccountManagementFragment; import org.chromium.chrome.browser.signin.SigninManager; import org.chromium.chrome.browser.signin.SigninManager.SignInCallback; import org.chromium.chrome.browser.util.FeatureUtilities; /** * A helper to perform all necessary steps for the automatic FRE sign in. * The helper performs any pending request to sign in from the First Run Experience. * The helper calls the observer's onSignInComplete() if * - nothing needs to be done, or when * - the sign in is complete. * If the sign in process fails or if an interactive FRE sequence is necessary, * the helper starts the FRE activity, finishes the current activity and calls * OnSignInCancelled. * * Usage: * FirstRunSignInProcessor.start(activity). */ public final class FirstRunSignInProcessor { private static final String TAG = "FirstRunSigninProc"; /** * SharedPreferences preference names to keep the state of the First Run Experience. */ private static final String FIRST_RUN_FLOW_SIGNIN_COMPLETE = "first_run_signin_complete"; // Needed by ChromeBackupAgent public static final String FIRST_RUN_FLOW_SIGNIN_SETUP = "first_run_signin_setup"; public static final String FIRST_RUN_FLOW_SIGNIN_ACCOUNT_NAME = "first_run_signin_account_name"; /** * Initiates the automatic sign-in process in background. * * @param activity The context for the FRE parameters processor. */ public static void start(final Activity activity) { SigninManager signinManager = SigninManager.get(activity.getApplicationContext()); signinManager.onFirstRunCheckDone(); boolean firstRunFlowComplete = FirstRunStatus.getFirstRunFlowComplete(activity); // We skip signin and the FRE if // - FRE is disabled, or // - FRE hasn't been completed, but the user has already seen the ToS in the Setup Wizard. if (CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE) || ApiCompatibilityUtils.isDemoUser(activity) || (!firstRunFlowComplete && ToSAckedReceiver.checkAnyUserHasSeenToS(activity))) { return; } // Force trigger the FRE if the Lightweight FRE is disabled or Chrome is started via Chrome // icon or via intent from GSA. Otherwise, skip signin. if (!firstRunFlowComplete) { if (!CommandLine.getInstance().hasSwitch( ChromeSwitches.ENABLE_LIGHTWEIGHT_FIRST_RUN_EXPERIENCE) || TextUtils.equals(activity.getIntent().getAction(), Intent.ACTION_MAIN) || IntentHandler.determineExternalIntentSource( activity.getPackageName(), activity.getIntent()) == ExternalAppId.GSA) { requestToFireIntentAndFinish(activity); } return; } // We are only processing signin from the FRE. if (getFirstRunFlowSignInComplete(activity)) { return; } final String accountName = getFirstRunFlowSignInAccountName(activity); if (!FeatureUtilities.canAllowSync(activity) || !signinManager.isSignInAllowed() || TextUtils.isEmpty(accountName)) { setFirstRunFlowSignInComplete(activity, true); return; } final boolean setUp = getFirstRunFlowSignInSetup(activity); signinManager.signIn(accountName, activity, new SignInCallback() { @Override public void onSignInComplete() { // Show sync settings if user pressed the "Settings" button. if (setUp) { openSignInSettings(activity); } setFirstRunFlowSignInComplete(activity, true); } @Override public void onSignInAborted() { // Set FRE as complete even if signin fails because the user has already seen and // accepted the terms of service. setFirstRunFlowSignInComplete(activity, true); } }); } /** * Opens sign in settings as requested in the FRE sign-in dialog. */ private static void openSignInSettings(Activity activity) { Intent intent = PreferencesLauncher.createIntentForSettingsPage( activity, AccountManagementFragment.class.getName()); activity.startActivity(intent); } /** * Starts the full FRE and finishes the current activity. */ private static void requestToFireIntentAndFinish(Activity activity) { Log.e(TAG, "Attempt to pass-through without completed FRE"); // Things went wrong -- we want the user to go through the full FRE. FirstRunStatus.setFirstRunFlowComplete(activity, false); setFirstRunFlowSignInComplete(activity, false); setFirstRunFlowSignInAccountName(activity, null); setFirstRunFlowSignInSetup(activity, false); activity.startActivity(FirstRunFlowSequencer.createGenericFirstRunIntent(activity, true)); } /** * @return Whether there is no pending sign-in requests from the First Run Experience. * @param context A context */ @VisibleForTesting public static boolean getFirstRunFlowSignInComplete(Context context) { return ContextUtils.getAppSharedPreferences() .getBoolean(FIRST_RUN_FLOW_SIGNIN_COMPLETE, false); } /** * Sets the "pending First Run Experience sign-in requests" preference. * @param context A context * @param isComplete Whether there is no pending sign-in requests from the First Run Experience. */ @VisibleForTesting public static void setFirstRunFlowSignInComplete(Context context, boolean isComplete) { ContextUtils.getAppSharedPreferences() .edit() .putBoolean(FIRST_RUN_FLOW_SIGNIN_COMPLETE, isComplete) .apply(); } /** * @return The account name selected during the First Run Experience, or null if none. * @param context A context */ private static String getFirstRunFlowSignInAccountName(Context context) { return ContextUtils.getAppSharedPreferences() .getString(FIRST_RUN_FLOW_SIGNIN_ACCOUNT_NAME, null); } /** * Sets the account name for the pending sign-in First Run Experience request. * @param context A context * @param accountName The account name, or null. */ private static void setFirstRunFlowSignInAccountName(Context context, String accountName) { ContextUtils.getAppSharedPreferences() .edit() .putString(FIRST_RUN_FLOW_SIGNIN_ACCOUNT_NAME, accountName) .apply(); } /** * @return Whether the user selected to see the settings once signed in after FRE. * @param context A context */ private static boolean getFirstRunFlowSignInSetup(Context context) { return ContextUtils.getAppSharedPreferences().getBoolean( FIRST_RUN_FLOW_SIGNIN_SETUP, false); } /** * Sets the preference to see the settings once signed in after FRE. * @param context A context * @param isComplete Whether the user selected to see the settings once signed in. */ private static void setFirstRunFlowSignInSetup(Context context, boolean isComplete) { ContextUtils.getAppSharedPreferences() .edit() .putBoolean(FIRST_RUN_FLOW_SIGNIN_SETUP, isComplete) .apply(); } /** * Finalize the state of the FRE flow (mark is as "complete" and finalize parameters). * @param context A context * @param data Resulting FRE properties bundle */ public static void finalizeFirstRunFlowState(Context context, Bundle data) { FirstRunStatus.setFirstRunFlowComplete(context, true); setFirstRunFlowSignInAccountName(context, data.getString(FirstRunActivity.RESULT_SIGNIN_ACCOUNT_NAME)); setFirstRunFlowSignInSetup( context, data.getBoolean(FirstRunActivity.RESULT_SHOW_SIGNIN_SETTINGS)); } /** * Allows the user to sign-in if there are no pending FRE sign-in requests. * @param context A context */ public static void updateSigninManagerFirstRunCheckDone(Context context) { SigninManager manager = SigninManager.get(context); if (manager.isSignInAllowed()) return; if (!FirstRunStatus.getFirstRunFlowComplete(context)) return; if (!getFirstRunFlowSignInComplete(context)) return; manager.onFirstRunCheckDone(); } }