Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1630)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java

Issue 978653002: Upstream FirstRunActivity and friends. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..6018e0b8f39035d9366a368397122cfda51a8427
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
@@ -0,0 +1,420 @@
+// 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.app.Fragment;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.ActionBarActivity;
+import android.text.TextUtils;
+
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromiumApplication;
+import org.chromium.chrome.browser.metrics.UmaBridge;
+import org.chromium.chrome.browser.profiles.Profile;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Handles the First Run Experience sequences shown to the user launching Chrome for the first time.
+ * It supports only a simple format of FRE:
+ * [Welcome]
+ * [Intro pages...]
+ * [Sign-in page]
+ * The activity might be run more than once, e.g. 1) for ToS and sign-in, and 2) for intro.
+ */
+public class FirstRunActivity extends ActionBarActivity implements FirstRunPageDelegate {
+ protected static final String TAG = "FirstRunActivity";
+
+ // Incoming parameters:
+ public static final String ORIGINAL_INTENT = "OriginalIntent";
+ public static final String FIRE_ORIGINAL_INTENT = "FireOriginalIntent";
+ public static final String COMING_FROM_CHROME_ICON = "ComingFromChromeIcon";
+ public static final String USE_FRE_FLOW_SEQUENCER = "UseFreFlowSequencer";
+
+ public static final String SHOW_WELCOME_PAGE = "ShowWelcome";
+ public static final String SKIP_WELCOME_PAGE_IF_ACCEPTED_TOS = "SkipWelcomePageIfAcceptedToS";
+ public static final String SHOW_INTRO_BITMAP = "ShowIntroBitmap";
+ public static final String SKIP_ALL_INTRO = "SkipAllIntro"; // Marks all intros as seen.
+ public static final String SHOW_SIGNIN_PAGE = "ShowSignIn";
+
+ // Outcoming results:
+ public static final String RESULT_CLOSE_APP = "Close App";
+ public static final String RESULT_SIGNIN_ACCOUNT_NAME = "ResultSignInTo";
+ public static final String RESULT_SHOW_SYNC_SETTINGS = "ResultShowSyncSettings";
+
+ @VisibleForTesting
+ static FirstRunGlue sGlue = new FirstRunGlueImpl();
+
+ private boolean mShowWelcomePage = true;
+
+ private String mResultSignInAccountName;
+ private boolean mResultShowSyncSettings;
+
+ // TODO(aurimas): make this private once all of FirstRunActivity is upstreamed.
+ protected boolean mNativeSideIsInitialized;
+
+ private ProfileDataCache mProfileDataCache;
+ private ViewPager mPager;
+
+ private Bundle mFreProperties;
+
+ private List<Callable<FirstRunPage>> mPages;
+ private int mSkipIntroPageNumber;
+
+ /**
+ * The pager adapter, which provides the pages to the view pager widget.
+ */
+ private FirstRunPagerAdapter mPagerAdapter;
+
+ /**
+ * Defines a sequence of pages to be shown (depending on parameters etc).
+ */
+ private void createPageSequence() {
+ mPages = new ArrayList<Callable<FirstRunPage>>();
+
+ // An optional welcome page.
+ if (mShowWelcomePage) mPages.add(pageOf(ToSAndUMAFirstRunFragment.class));
+
+ // An optional sequence of intro pages.
+ if (!mFreProperties.getBoolean(SKIP_ALL_INTRO)) {
+ final long bitmap = mFreProperties.getLong(SHOW_INTRO_BITMAP);
+
+ // "Hera" (recents/tabs) promo.
+ if (((bitmap & FirstRunIntroPage.INTRO_RECENTS) != 0)
+ && sGlue.isDocumentModeEligible(getApplicationContext())) {
+ mPages.add(pageOf(FirstRunIntroRecentsPage.class));
+ }
+ }
+
+ // Set the anchor to jump to if the user skips intro pages.
+ mSkipIntroPageNumber = mPages.size();
+
+ // An optional sign-in page.
+ if (mFreProperties.getBoolean(SHOW_SIGNIN_PAGE)) {
+ mPages.add(pageOf(AccountFirstRunFragment.class));
+ }
+ }
+
+ // Activity:
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ initializeBrowserProcess();
+
+ super.onCreate(savedInstanceState);
+ setFinishOnTouchOutside(false);
+
+ if (savedInstanceState != null) {
+ mFreProperties = savedInstanceState;
+ } else if (getIntent() != null) {
+ mFreProperties = getIntent().getExtras();
+ } else {
+ mFreProperties = new Bundle();
+ }
+
+ mPager = new ViewPager(this);
+ mPager.setId(R.id.fre_pager);
+ setContentView(mPager);
+ android.util.Log.i("FirstRunActivity", "onCreate: after setContentView");
+
+ mProfileDataCache = new ProfileDataCache(FirstRunActivity.this, null);
+ mProfileDataCache.setProfile(Profile.getLastUsedProfile());
+ new FirstRunFlowSequencer(this, mFreProperties) {
+ @Override
+ public void onFlowIsKnown(Activity activity, Bundle freProperties) {
+ android.util.Log.i("FirstRunActivity", "in onFlowIsKnown");
+
+ if (freProperties == null) {
+ completeFirstRunExperience();
+ return;
+ }
+
+ mFreProperties = freProperties;
+ mShowWelcomePage = mFreProperties.getBoolean(SHOW_WELCOME_PAGE);
+ if (mShowWelcomePage
+ && mFreProperties.getBoolean(SKIP_WELCOME_PAGE_IF_ACCEPTED_TOS)) {
+ mShowWelcomePage = !sGlue.didAcceptTermsOfService(getApplicationContext());
+ }
+ createPageSequence();
+
+ if (TextUtils.isEmpty(mResultSignInAccountName)) {
+ mResultSignInAccountName = mFreProperties.getString(
+ AccountFirstRunFragment.FORCE_SIGNIN_ACCOUNT_TO);
+ }
+
+ if (mPages.size() == 0) {
+ completeFirstRunExperience();
+ return;
+ }
+
+ mPagerAdapter =
+ new FirstRunPagerAdapter(getFragmentManager(), mPages, mFreProperties);
+ stopProgressionIfNotAcceptedTermsOfService();
+ mPager.setAdapter(mPagerAdapter);
+
+ skipPagesIfNecessary();
+ android.util.Log.i("FirstRunActivity", "before return from onFlowIsKnown");
+ }
+ }.start();
+ android.util.Log.i("FirstRunActivity", "onCreate: after starting the sequencer");
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putAll(mFreProperties);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ flushPersistentData();
+ }
+
+ @Override
+ protected void onResumeFragments() {
+ skipPagesIfNecessary();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mProfileDataCache.onDestroy();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ stopProgressionIfNotAcceptedTermsOfService();
+ if (!mFreProperties.getBoolean(USE_FRE_FLOW_SEQUENCER)) {
+ if (FirstRunStatus.getFirstRunFlowComplete(this)
+ && FirstRunIntroPage.wereAllNecessaryPagesShown(this)) {
+ // This is a parallel flow that needs to be refreshed/re-fired.
+ // Signal the FRE flow completion and re-launch the original intent.
+ completeFirstRunExperience();
+ }
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ // Terminate if we are still waiting for the native or for Android EDU / GAIA Child checks.
+ if (mPagerAdapter == null) {
+ abortFirstRunExperience();
+ return;
+ }
+
+ Object currentItem = mPagerAdapter.instantiateItem(mPager, mPager.getCurrentItem());
+ if (currentItem instanceof FirstRunPage) {
+ FirstRunPage page = (FirstRunPage) currentItem;
+ if (page.interceptBackPressed()) return;
+ }
+
+ if (mPager.getCurrentItem() == 0) {
+ abortFirstRunExperience();
+ } else {
+ mPager.setCurrentItem(mPager.getCurrentItem() - 1);
+ }
+ }
+
+ // FirstRunPageDelegate:
+
+ @Override
+ public ProfileDataCache getProfileDataCache() {
+ return mProfileDataCache;
+ }
+
+ @Override
+ public void advanceToNextPage() {
+ jumpToPage(mPager.getCurrentItem() + 1, true);
+ }
+
+ @Override
+ public void skipIntroPages() {
+ jumpToPage(mSkipIntroPageNumber, false);
+ }
+
+ @Override
+ public void recreateCurrentPage() {
+ mPagerAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void abortFirstRunExperience() {
+ Intent intent = new Intent();
+ if (mFreProperties != null) intent.putExtras(mFreProperties);
+ intent.putExtra(RESULT_CLOSE_APP, true);
+ finishAllFREActivities(Activity.RESULT_CANCELED, intent);
+ }
+
+ @Override
+ public void completeFirstRunExperience() {
+ if (!TextUtils.isEmpty(mResultSignInAccountName)) {
+ UmaBridge.freSignInAccepted(
+ mResultShowSyncSettings,
+ sGlue.isDefaultAccountName(getApplicationContext(), mResultSignInAccountName));
+ }
+
+ mFreProperties.putString(RESULT_SIGNIN_ACCOUNT_NAME, mResultSignInAccountName);
+ mFreProperties.putBoolean(RESULT_SHOW_SYNC_SETTINGS, mResultShowSyncSettings);
+ FirstRunFlowSequencer.markFlowAsCompleted(this, mFreProperties);
+
+ if (mFreProperties.getBoolean(FirstRunActivity.FIRE_ORIGINAL_INTENT)) {
+ Intent originalIntent = mFreProperties.getParcelable(FirstRunActivity.ORIGINAL_INTENT);
+ startActivity(originalIntent);
+ }
+
+ Intent resultData = new Intent();
+ resultData.putExtras(mFreProperties);
+ finishAllFREActivities(Activity.RESULT_OK, resultData);
+ }
+
+ @Override
+ public void onSigninDialogShown() {
+ UmaBridge.freSignInShown();
+ }
+
+ @Override
+ public void refuseSignIn() {
+ UmaBridge.freSkipSignIn();
+ UmaBridge.freSignInDeclined();
+ mResultSignInAccountName = null;
+ mResultShowSyncSettings = false;
+ }
+
+ @Override
+ public void acceptSignIn(String accountName) {
+ mResultSignInAccountName = accountName;
+ }
+
+ @Override
+ public void askToOpenSyncSettings() {
+ mResultShowSyncSettings = true;
+ }
+
+ @Override
+ public boolean didAcceptTermsOfService() {
+ return sGlue.didAcceptTermsOfService(getApplicationContext());
+ }
+
+ @Override
+ public boolean isNeverUploadCrashDump() {
+ return sGlue.isNeverUploadCrashDump(getApplicationContext());
+ }
+
+ @Override
+ public void acceptTermsOfService(boolean allowCrashUpload) {
+ sGlue.acceptTermsOfService(getApplicationContext(), allowCrashUpload);
+ flushPersistentData();
+ stopProgressionIfNotAcceptedTermsOfService();
+ jumpToPage(mPager.getCurrentItem() + 1, true);
+ }
+
+ @Override
+ public void openAccountAdder(Fragment fragment) {
+ sGlue.openAccountAdder(fragment);
+ }
+
+ protected void flushPersistentData() {
+ if (mNativeSideIsInitialized) ChromiumApplication.flushPersistentData();
+ }
+
+ private static void finishAllFREActivities(int result, Intent data) {
+ List<WeakReference<Activity>> activities = ApplicationStatus.getRunningActivities();
+ for (WeakReference<Activity> weakActivity : activities) {
+ Activity activity = weakActivity.get();
+ if (activity instanceof FirstRunActivity) {
+ activity.setResult(result, data);
+ activity.finish();
+ }
+ }
+ }
+
+
+ /**
+ * Transitions to a given page.
+ * @return Whether the transition to a given page was allowed.
+ * @param position A page index to transition to.
+ * @param smooth Whether the transition should be smooth.
+ */
+ private boolean jumpToPage(int position, boolean smooth) {
+ if (mShowWelcomePage && !didAcceptTermsOfService()) {
+ return position == 0;
+ }
+ if (position >= mPagerAdapter.getCount()) {
+ completeFirstRunExperience();
+ return false;
+ }
+ mPager.setCurrentItem(position, smooth);
+ return true;
+ }
+
+ private void stopProgressionIfNotAcceptedTermsOfService() {
+ if (mPagerAdapter == null) return;
+ mPagerAdapter.setStopAtTheFirstPage(mShowWelcomePage && !didAcceptTermsOfService());
+ }
+
+ private void skipPagesIfNecessary() {
+ if (mPagerAdapter == null) return;
+
+ int currentPageIndex = mPager.getCurrentItem();
+ while (currentPageIndex < mPagerAdapter.getCount()) {
+ FirstRunPage currentPage = (FirstRunPage) mPagerAdapter.getItem(currentPageIndex);
+ if (!currentPage.shouldSkipPageOnResume(getApplicationContext())) return;
+ if (!jumpToPage(currentPageIndex + 1, false)) return;
+ currentPageIndex = mPager.getCurrentItem();
+ }
+ }
+
+ protected void initializeBrowserProcess() {
+ // TODO(aurimas): implement this once browser process initialization is upstreamed.
+ }
+
+ /**
+ * Creates a trivial page constructor for a given page type.
+ * @param clazz The .class of the page type.
+ * @return The simple constructor for a given page type (no parameters, no tuning).
+ */
+ public static Callable<FirstRunPage> pageOf(final Class<? extends FirstRunPage> clazz) {
+ return new Callable<FirstRunPage>() {
+ @Override
+ public FirstRunPage call() throws Exception {
+ Constructor<? extends FirstRunPage> constructor = clazz.getDeclaredConstructor();
+ return constructor.newInstance();
+ }
+ };
+ }
+
+ @Override
+ public void showEmbedContentViewActivity(int title, int url) {
+ // TODO(aurimas): implement this once EmbededContentViewActivity is upstreamed.
+ }
+
+ public void showSignInNotification() {
+ // TODO(aurimas): implement this once GoogleServicesManager is upstreamed.
+ }
+
+ @Override
+ public void openDocumentModeSettings() {
+ // TODO(aurimas): implement opening settings once DocumentModeSettings is upstreamed.
+ }
+
+ /**
+ * Open Chrome Sync settings page.
+ * @param accountName the name of the account that is being synced.
+ */
+ public void openSyncSettings(String accountName) {
+ // TODO(aurimas): implement opening settings once SyncCustomizationFragment is upstreamed.
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698