| Index: chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..babf92f1cc174432620f3ff31b65edeb3a34ba39
|
| --- /dev/null
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
|
| @@ -0,0 +1,259 @@
|
| +// 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.accounts.Account;
|
| +import android.app.Activity;
|
| +import android.content.Context;
|
| +import android.content.Intent;
|
| +import android.content.pm.ApplicationInfo;
|
| +import android.os.Bundle;
|
| +
|
| +import org.chromium.base.ApiCompatibilityUtils;
|
| +import org.chromium.base.CommandLine;
|
| +import org.chromium.base.VisibleForTesting;
|
| +import org.chromium.chrome.ChromeSwitches;
|
| +import org.chromium.chrome.browser.ChromeVersionInfo;
|
| +import org.chromium.chrome.browser.ChromiumApplication;
|
| +import org.chromium.chrome.browser.preferences.PrefServiceBridge;
|
| +import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager;
|
| +import org.chromium.chrome.browser.services.AndroidEduAndChildAccountHelper;
|
| +import org.chromium.chrome.browser.signin.SigninManager;
|
| +import org.chromium.chrome.browser.util.FeatureUtilities;
|
| +import org.chromium.sync.signin.AccountManagerHelper;
|
| +import org.chromium.sync.signin.ChromeSigninController;
|
| +
|
| +/**
|
| + * A helper to determine what should be the sequence of First Run Experience screens.
|
| + * Usage:
|
| + * new FirstRunFlowSequencer(activity, launcherProvidedProperties) {
|
| + * override onFlowIsKnown
|
| + * }.start();
|
| + */
|
| +public abstract class FirstRunFlowSequencer {
|
| + private final Activity mActivity;
|
| + private final Bundle mLaunchProperties;
|
| +
|
| + private boolean mIsAndroidEduDevice;
|
| + private boolean mHasChildAccount;
|
| +
|
| + /**
|
| + * Callback that is called once the flow is determined.
|
| + * If the properties is null, the First Run experience needs to finish and
|
| + * restart the original intent if necessary.
|
| + * @param activity An activity.
|
| + * @param freProperties Properties to be used in the First Run activity, or null.
|
| + */
|
| + public abstract void onFlowIsKnown(Activity activity, Bundle freProperties);
|
| +
|
| + public FirstRunFlowSequencer(Activity activity, Bundle launcherProvidedProperties) {
|
| + mActivity = activity;
|
| + mLaunchProperties = launcherProvidedProperties;
|
| + }
|
| +
|
| + /**
|
| + * Starts determining parameters for the First Run.
|
| + * Once finished, calls onFlowIsKnown().
|
| + */
|
| + public void start() {
|
| + if (CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)) {
|
| + onFlowIsKnown(mActivity, null);
|
| + return;
|
| + }
|
| +
|
| + if (!mLaunchProperties.getBoolean(FirstRunActivity.USE_FRE_FLOW_SEQUENCER)) {
|
| + onFlowIsKnown(mActivity, mLaunchProperties);
|
| + return;
|
| + }
|
| +
|
| + new AndroidEduAndChildAccountHelper() {
|
| + @Override
|
| + public void onParametersReady() {
|
| + mIsAndroidEduDevice = isAndroidEduDevice();
|
| + mHasChildAccount = hasChildAccount();
|
| + processFreEnvironment();
|
| + }
|
| + }.start(mActivity);
|
| + }
|
| +
|
| + /**
|
| + * @return Whether the sync could be turned on.
|
| + */
|
| + @VisibleForTesting
|
| + boolean isSyncAllowed() {
|
| + return FeatureUtilities.canAllowSync(mActivity)
|
| + && !SigninManager.get(mActivity.getApplicationContext()).isSigninDisabledByPolicy();
|
| + }
|
| +
|
| + /**
|
| + * @return Whether Terms of Service could be assumed to be accepted.
|
| + */
|
| + @VisibleForTesting
|
| + boolean didAcceptToS() {
|
| + return ToSAckedReceiver.checkAnyUserHasSeenToS(mActivity)
|
| + || PrefServiceBridge.getInstance().isFirstRunEulaAccepted();
|
| + }
|
| +
|
| + /**
|
| + * @return Whether Chrome was installed as a part of the system image.
|
| + */
|
| + @VisibleForTesting
|
| + boolean isSystemInstall() {
|
| + return ((mActivity.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) != 0);
|
| + }
|
| +
|
| + private void processFreEnvironment() {
|
| + final Context context = mActivity.getApplicationContext();
|
| +
|
| + if (FirstRunStatus.getFirstRunFlowComplete(mActivity)
|
| + && FirstRunIntroPage.wereAllNecessaryPagesShown(mActivity)) {
|
| + assert PrefServiceBridge.getInstance().isFirstRunEulaAccepted();
|
| + // We do not need any interactive FRE.
|
| + onFlowIsKnown(mActivity, null);
|
| + return;
|
| + }
|
| +
|
| + Bundle freProperties = new Bundle();
|
| + freProperties.putAll(mLaunchProperties);
|
| + freProperties.remove(FirstRunActivity.USE_FRE_FLOW_SEQUENCER);
|
| +
|
| + final Account[] googleAccounts = AccountManagerHelper.get(context).getGoogleAccounts();
|
| + final boolean onlyOneAccount = googleAccounts.length == 1;
|
| +
|
| + // EDU devices should always have exactly 1 google account, which will be automatically
|
| + // signed-in. All FRE screens are skipped in this case.
|
| + final boolean forceEduSignIn = mIsAndroidEduDevice
|
| + && onlyOneAccount
|
| + && !ChromeSigninController.get(context).isSignedIn();
|
| +
|
| + final boolean shouldSkipFirstUseHints =
|
| + ApiCompatibilityUtils.shouldSkipFirstUseHints(context.getContentResolver());
|
| +
|
| + if (!FirstRunStatus.getFirstRunFlowComplete(context)) {
|
| + // In the full FRE we always show the Welcome page, except on EDU devices.
|
| + final boolean showWelcomePage = !forceEduSignIn;
|
| + freProperties.putBoolean(FirstRunActivity.SHOW_WELCOME_PAGE, showWelcomePage);
|
| +
|
| + // Enable reporting by default on non-Stable releases.
|
| + // The user can turn it off on the Welcome page.
|
| + // This is controlled by the administrator via a policy on EDU devices.
|
| + if (!ChromeVersionInfo.isStableBuild()) {
|
| + PrivacyPreferencesManager.getInstance(mActivity).initCrashUploadPreference(true);
|
| + }
|
| +
|
| + // We show the sign-in page if sync is allowed, and this is not an EDU device, and
|
| + // - no "skip the first use hints" is set, or
|
| + // - "skip the first use hints" is set, but there is at least one account.
|
| + final boolean syncOk = isSyncAllowed();
|
| + final boolean offerSignInOk = syncOk
|
| + && !forceEduSignIn
|
| + && (!shouldSkipFirstUseHints || googleAccounts.length > 0);
|
| + freProperties.putBoolean(FirstRunActivity.SHOW_SIGNIN_PAGE, offerSignInOk);
|
| +
|
| + if (offerSignInOk || forceEduSignIn) {
|
| + // If the user has accepted the ToS in the Setup Wizard and there is exactly
|
| + // one account, or if the device has a child account, or if the device is an
|
| + // Android EDU device and there is exactly one account, preselect the sign-in
|
| + // account and force the selection if necessary.
|
| + if ((ToSAckedReceiver.checkAnyUserHasSeenToS(mActivity) && onlyOneAccount)
|
| + || mHasChildAccount
|
| + || forceEduSignIn) {
|
| + freProperties.putString(AccountFirstRunFragment.FORCE_SIGNIN_ACCOUNT_TO,
|
| + googleAccounts[0].name);
|
| + freProperties.putBoolean(AccountFirstRunFragment.PRESELECT_BUT_ALLOW_TO_CHANGE,
|
| + !forceEduSignIn && !mHasChildAccount);
|
| + }
|
| + }
|
| + } else {
|
| + // If the full FRE has already been shown, don't show Welcome or Sign-In pages.
|
| + freProperties.putBoolean(FirstRunActivity.SHOW_WELCOME_PAGE, false);
|
| + freProperties.putBoolean(FirstRunActivity.SHOW_SIGNIN_PAGE, false);
|
| + }
|
| +
|
| + // We always show all not-yet-shown intro pages that could be shown.
|
| + freProperties.putLong(FirstRunActivity.SHOW_INTRO_BITMAP,
|
| + FirstRunIntroPage.getAllPresentablePages(mActivity)
|
| + & ~FirstRunIntroPage.getAlreadyShownPagesBitmap(mActivity));
|
| + freProperties.putBoolean(FirstRunActivity.SKIP_ALL_INTRO,
|
| + shouldSkipFirstUseHints || forceEduSignIn);
|
| + freProperties.putBoolean(AccountFirstRunFragment.IS_CHILD_ACCOUNT, mHasChildAccount);
|
| +
|
| + onFlowIsKnown(mActivity, freProperties);
|
| + }
|
| +
|
| + /**
|
| + * Marks a given flow as completed.
|
| + * @param activity An activity.
|
| + * @param data Resulting FRE properties bundle.
|
| + */
|
| + public static void markFlowAsCompleted(Activity activity, Bundle data) {
|
| + // When the user accepts ToS in the Setup Wizard (see ToSAckedReceiver), we do not
|
| + // show the ToS page to the user because the user has already accepted one outside FRE.
|
| + if (!PrefServiceBridge.getInstance().isFirstRunEulaAccepted()) {
|
| + PrefServiceBridge.getInstance().setEulaAccepted();
|
| + }
|
| +
|
| + // Mark the intro pages we have shown as complete.
|
| + FirstRunIntroPage.markAsShown(activity, data.getLong(FirstRunActivity.SHOW_INTRO_BITMAP));
|
| +
|
| + // Mark the FRE flow as complete and set the sign-in flow preferences if necessary.
|
| + FirstRunSignInProcessor.finalizeFirstRunFlowState(activity, data);
|
| + }
|
| +
|
| + /**
|
| + * Checks if the First Run needs to be launched.
|
| + * @return The intent to launch the First Run Experience if necessary, or null.
|
| + * @param activity The context
|
| + * @param originalIntent An original intent
|
| + * @param fromChromeIcon Whether Chrome is opened via the Chrome icon
|
| + */
|
| + public static Intent checkIfFirstRunIsNecessary(Activity activity,
|
| + Intent originalIntent, boolean fromChromeIcon) {
|
| + // If FRE is disabled (e.g. in tests), proceed directly to the intent handling.
|
| + if (CommandLine.getInstance().hasSwitch(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)) {
|
| + return null;
|
| + }
|
| +
|
| + // If Chrome isn't opened via the Chrome icon, and the user accepted the ToS
|
| + // in the Setup Wizard, skip any First Run Experience screens and proceed directly
|
| + // to the intent handling.
|
| + if (!fromChromeIcon && ToSAckedReceiver.checkAnyUserHasSeenToS(activity)) return null;
|
| +
|
| + // If the user hasn't been through the First Run Activity -- it must be shown.
|
| + final boolean baseFreComplete = FirstRunStatus.getFirstRunFlowComplete(activity);
|
| + if (!baseFreComplete) {
|
| + return createGenericFirstRunIntent(activity, originalIntent, fromChromeIcon);
|
| + }
|
| +
|
| + // If Chrome isn't opened via the Chrome icon, or the user has already been through all
|
| + // intro screens, proceed directly to the intent handling.
|
| + if (!fromChromeIcon || FirstRunIntroPage.wereAllNecessaryPagesShown(activity)) return null;
|
| +
|
| + // Otherwise (Chrome is launched via the Chrome icon and the user has already been through
|
| + // the First Run Activity, but not through all intro screens), check if we could show some
|
| + // intro screen.
|
| + if (FirstRunIntroPage.getAllPresentablePages(activity) == 0L) return null;
|
| +
|
| + return createGenericFirstRunIntent(activity, originalIntent, fromChromeIcon);
|
| + }
|
| +
|
| + /**
|
| + * @return A generic intent to show the First Run Activity.
|
| + * @param activity The context
|
| + * @param originalIntent An original intent
|
| + * @param fromChromeIcon Whether Chrome is opened via the Chrome icon
|
| + */
|
| + public static Intent createGenericFirstRunIntent(
|
| + Activity activity, Intent originalIntent, boolean fromChromeIcon) {
|
| + ChromiumApplication application = (ChromiumApplication) activity.getApplication();
|
| + String activityName = application.getFirstRunActivityName();
|
| +
|
| + Intent intent = new Intent();
|
| + intent.setClassName(activity, activityName);
|
| + intent.putExtra(FirstRunActivity.COMING_FROM_CHROME_ICON, fromChromeIcon);
|
| + intent.putExtra(FirstRunActivity.USE_FRE_FLOW_SEQUENCER, true);
|
| + return intent;
|
| + }
|
| +}
|
|
|