From de49c2d22f6c873aac94696ef004ecf8d8dd6b52 Mon Sep 17 00:00:00 2001 From: dfalcantara Date: Fri, 25 Mar 2016 16:37:20 -0700 Subject: Add pathway to auto-migrate users when Chrome launches * For now, a command line flag is required to trigger this pathway: --enable-forced-migration * UpgradeActivity kicks off the DocumentModeAssassin and waits for it to finish. This Activity can only be launched via other ChromeActivities, and can be passed an Intent to refire when the migration finishes. * A MAIN Intent or Intent to start a new DocumentActivity that goes through ChromeLauncherActivity will force users through UgpradeActivity, instead. This causes document mode to be flipped off, and anything going down the ChromeLauncherActivity pathway again should no longer trigger migration. This pathway catches new tabs created via VIEW Intents, MAIN Intents, CCT Intents, and (sometimes) WebappActivity Intents. * AsyncInitializationActivity watches for the need for the migration pathway to be triggered when a ChromeActivity subclass starts up. This pathway catches Intents that skip ChromeLauncherActivity, which includes ChromeTabbedActivity, DocumentActivity, CustomTabActivity, and WebappActivity. These can be launched directly by selecting them from Android Recents, or by firing Intents to bring them back specifically. * The look of this Activity is a stand-in for when we get redlines. The animation is also kind of janky, but I'm aiming for functionality at this point. * TabDelegate is no longer allowed to create new DocumentActivities, instead deferring down the ChromeLauncherActivity pathway. This catches cases with push notifications triggering a tab to be created when they're clicked. * The preference to enable document mode is hidden once the user has migrated. BUG=582539 Review URL: https://codereview.chromium.org/1817083003 Cr-Commit-Position: refs/heads/master@{#383406} --- chrome/android/java/AndroidManifest.xml | 20 +++ .../android/java/res/layout/upgrade_activity.xml | 52 ++++++++ .../chromium/chrome/browser/ChromeSwitches.java | 5 + .../chrome/browser/ChromeTabbedActivity.java | 3 + .../chromium/chrome/browser/UpgradeActivity.java | 136 +++++++++++++++++++++ .../browser/document/ChromeLauncherActivity.java | 28 +++-- .../chrome/browser/document/DocumentActivity.java | 24 +++- .../browser/init/AsyncInitializationActivity.java | 13 ++ .../browser/preferences/MainPreferences.java | 11 +- .../browser/tabmodel/DocumentModeAssassin.java | 81 +++++++----- .../browser/tabmodel/document/TabDelegate.java | 4 +- chrome/android/java_sources.gni | 1 + .../browser/tabmodel/DocumentModeAssassinTest.java | 9 +- ui/android/java/strings/android_ui_strings.grd | 5 + 14 files changed, 348 insertions(+), 44 deletions(-) create mode 100644 chrome/android/java/res/layout/upgrade_activity.xml create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/UpgradeActivity.java diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml index 4691aac..144e8e1 100644 --- a/chrome/android/java/AndroidManifest.xml +++ b/chrome/android/java/AndroidManifest.xml @@ -201,6 +201,21 @@ by a child template that "extends" this file. + + + + + + + + + + + diff --git a/chrome/android/java/res/layout/upgrade_activity.xml b/chrome/android/java/res/layout/upgrade_activity.xml new file mode 100644 index 0000000..975cf93 --- /dev/null +++ b/chrome/android/java/res/layout/upgrade_activity.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java index 534fdef..07c8b0f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java @@ -231,6 +231,11 @@ public abstract class ChromeSwitches { "disable-web-notification-custom-layouts"; /** + * Forces a user down the document to tabbed mode migration pathway. + */ + public static final String ENABLE_FORCED_MIGRATION = "enable-forced-migration"; + + /** * Determines which of the Herb prototypes is being tested. * See about:flags for descriptions. */ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 3d64a3f..e2bf87d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java @@ -725,6 +725,9 @@ public class ChromeTabbedActivity extends ChromeActivity implements OverviewMode if (tabIndex != TabModel.INVALID_TAB_INDEX) { getTabModelSelector().selectModel(otherModel.isIncognito()); TabModelUtils.setIndex(otherModel, tabIndex); + } else { + Log.e(TAG, "Failed to bring tab to front because it doesn't exist."); + return; } } else { TabModelUtils.setIndex(tabModel, tabIndex); diff --git a/chrome/android/java/src/org/chromium/chrome/browser/UpgradeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/UpgradeActivity.java new file mode 100644 index 0000000..e203e67 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/UpgradeActivity.java @@ -0,0 +1,136 @@ +// Copyright 2016 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; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.v7.app.AppCompatActivity; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.ApplicationStatus; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.tabmodel.DocumentModeAssassin; +import org.chromium.chrome.browser.tabmodel.DocumentModeAssassin.DocumentModeAssassinObserver; +import org.chromium.chrome.browser.util.IntentUtils; + +/** + * Activity that interrupts launch and shows that users are being upgraded to a new version of + * Chrome. + * + * TODO(dfalcantara): Do we need to worry about onNewIntent()? + */ +public class UpgradeActivity extends AppCompatActivity { + public static final String EXTRA_INTENT_TO_REFIRE = + "org.chromium.chrome.browser.INTENT_TO_REFIRE"; + + private static final long MIN_MS_TO_DISPLAY_ACTIVITY = 500; + private static final long INVALID_TIMESTAMP = -1; + + private final Handler mHandler; + private final DocumentModeAssassinObserver mObserver; + + private Intent mIntentToFireAfterUpgrade; + private long mStartTimestamp = INVALID_TIMESTAMP; + private boolean mIsDestroyed; + + public static void launchInstance(Context context, Intent originalIntent) { + Intent intent = new Intent(); + intent.setClass(context, UpgradeActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(UpgradeActivity.EXTRA_INTENT_TO_REFIRE, originalIntent); + context.startActivity(intent); + } + + public UpgradeActivity() { + mHandler = new Handler(Looper.getMainLooper()); + + mObserver = new DocumentModeAssassinObserver() { + @Override + public void onStageChange(int newStage) { + if (newStage != DocumentModeAssassin.STAGE_DONE) return; + DocumentModeAssassin.getInstance().removeObserver(this); + + // Always post to avoid any issues that could arise from firing the Runnable + // while other Observers are being alerted. + long msElapsed = System.currentTimeMillis() - mStartTimestamp; + long msRemaining = Math.max(0, MIN_MS_TO_DISPLAY_ACTIVITY - msElapsed); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + continueApplicationLaunch(); + } + }, msRemaining); + } + }; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mIntentToFireAfterUpgrade = getIntentToFireAfterUpgrade(getIntent()); + setContentView(R.layout.upgrade_activity); + + DocumentModeAssassin assassin = DocumentModeAssassin.getInstance(); + if (!DocumentModeAssassin.isMigrationNecessary() + || assassin.getStage() == DocumentModeAssassin.STAGE_DONE) { + // Migration finished in the background. + continueApplicationLaunch(); + } else { + // Kick off migration if it hasn't already started. + assassin.addObserver(mObserver); + assassin.migrateFromDocumentToTabbedMode(); + } + } + + @Override + protected void onResume() { + super.onResume(); + + // Set the timestamp after the Activity is visible to avoid shortening the timer. + if (mStartTimestamp == INVALID_TIMESTAMP) mStartTimestamp = System.currentTimeMillis(); + } + + @Override + protected void onDestroy() { + mIsDestroyed = true; + super.onDestroy(); + } + + private static Intent getIntentToFireAfterUpgrade(Intent activityIntent) { + Intent intentToFire = null; + + // Retrieve the Intent that caused the user to end up on the upgrade pathway. + if (activityIntent != null) { + intentToFire = (Intent) IntentUtils.safeGetParcelableExtra( + activityIntent, EXTRA_INTENT_TO_REFIRE); + } + + // If there's no Intent to refire, send them to the browser. + if (intentToFire == null) { + intentToFire = new Intent(Intent.ACTION_MAIN); + intentToFire.setPackage(ApplicationStatus.getApplicationContext().getPackageName()); + } + + // Fire the Intent into a different task so that this one can go away. + intentToFire.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + + return intentToFire; + } + + private void continueApplicationLaunch() { + if (mIsDestroyed) return; + + ApiCompatibilityUtils.finishAndRemoveTask(this); + if (mIntentToFireAfterUpgrade != null && ApplicationStatus.hasVisibleActivities()) { + startActivity(mIntentToFireAfterUpgrade); + overridePendingTransition(android.R.anim.fade_in, 0); + mIntentToFireAfterUpgrade = null; + } + } +} diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java index c314adf..cd495fa 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java @@ -42,6 +42,7 @@ import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.IntentHandler.TabOpenType; import org.chromium.chrome.browser.ShortcutHelper; import org.chromium.chrome.browser.ShortcutSource; +import org.chromium.chrome.browser.UpgradeActivity; import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.WarmupManager; import org.chromium.chrome.browser.customtabs.CustomTabActivity; @@ -59,6 +60,7 @@ import org.chromium.chrome.browser.preferences.DocumentModeManager; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabIdManager; import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager; +import org.chromium.chrome.browser.tabmodel.DocumentModeAssassin; import org.chromium.chrome.browser.tabmodel.document.ActivityDelegate; import org.chromium.chrome.browser.tabmodel.document.AsyncTabCreationParams; import org.chromium.chrome.browser.tabmodel.document.DocumentTabModel; @@ -217,13 +219,6 @@ public class ChromeLauncherActivity extends Activity return; } - // Check if we're just closing all of the Incognito tabs. - if (TextUtils.equals(intent.getAction(), ACTION_CLOSE_ALL_INCOGNITO)) { - ChromeApplication.getDocumentTabModelSelector().getModel(true).closeAllTabs(); - ApiCompatibilityUtils.finishAndRemoveTask(this); - return; - } - // Check if we should launch the FirstRunActivity. This occurs after the check to launch // ChromeTabbedActivity because ChromeTabbedActivity handles FRE in its own way. if (launchFirstRunExperience()) return; @@ -234,8 +229,23 @@ public class ChromeLauncherActivity extends Activity return; } - // Launch a DocumentActivity to handle the Intent. - handleDocumentActivityIntent(); + if (DocumentModeAssassin.isMigrationNecessary()) { + Log.d(TAG, "Diverting to UpgradeActivity via ChromeLauncherActivity."); + UpgradeActivity.launchInstance(this, intent); + ApiCompatibilityUtils.finishAndRemoveTask(this); + return; + } else { + // Check if we're just closing all of the Incognito tabs. + if (TextUtils.equals(intent.getAction(), ACTION_CLOSE_ALL_INCOGNITO)) { + ChromeApplication.getDocumentTabModelSelector().getModel(true).closeAllTabs(); + ApiCompatibilityUtils.finishAndRemoveTask(this); + return; + } + + // Launch a DocumentActivity to handle the Intent. + handleDocumentActivityIntent(); + } + if (!mIsFinishDelayed) ApiCompatibilityUtils.finishAndRemoveTask(this); } diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java index eee242a..62d9864 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java @@ -230,8 +230,30 @@ public class DocumentActivity extends ChromeActivity { @Override protected boolean isStartedUpCorrectly(Intent intent) { - int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); boolean isDocumentMode = FeatureUtilities.isDocumentMode(this); + int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); + + if (!isDocumentMode) { + // Fire a MAIN Intent to send the user back through ChromeLauncherActivity. + Log.e(TAG, "User is not in document mode. Sending back to ChromeLauncherActivity."); + + // Try to bring this tab forward after migration. + Intent tabbedIntent = null; + if (tabId != Tab.INVALID_TAB_ID) tabbedIntent = Tab.createBringTabToFrontIntent(tabId); + + if (tabbedIntent == null) { + tabbedIntent = new Intent(Intent.ACTION_MAIN); + tabbedIntent.setPackage(getPackageName()); + } + + // Launch the other Activity in its own task so it stays when this one finishes. + tabbedIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + tabbedIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + + startActivity(tabbedIntent); + overridePendingTransition(0, 0); + } + boolean isStartedUpCorrectly = tabId != Tab.INVALID_TAB_ID && isDocumentMode; if (!isStartedUpCorrectly) { Log.e(TAG, "Discarding Intent: Tab = " + tabId + ", Document mode = " + isDocumentMode); diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java index 2c5d3a8..bfd5880 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java @@ -27,10 +27,12 @@ import org.chromium.base.TraceEvent; import org.chromium.base.library_loader.LoaderErrors; import org.chromium.base.library_loader.ProcessInitException; import org.chromium.chrome.browser.ChromeApplication; +import org.chromium.chrome.browser.UpgradeActivity; import org.chromium.chrome.browser.WarmupManager; import org.chromium.chrome.browser.metrics.LaunchMetrics; import org.chromium.chrome.browser.metrics.MemoryUma; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.tabmodel.DocumentModeAssassin; import org.chromium.ui.base.DeviceFormFactor; import java.lang.reflect.Field; @@ -189,6 +191,17 @@ public abstract class AsyncInitializationActivity extends AppCompatActivity impl */ @Override protected final void onCreate(Bundle savedInstanceState) { + if (DocumentModeAssassin.isMigrationNecessary()) { + super.onCreate(null); + + // Kick the user to the MigrationActivity. + UpgradeActivity.launchInstance(this, getIntent()); + + // Don't remove this task -- it may be a DocumentActivity that exists only in Recents. + finish(); + return; + } + if (!isStartedUpCorrectly(getIntent())) { sBadIntentMetric.recordHit(); super.onCreate(null); diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java index 2e655d14..cf5469a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java @@ -11,7 +11,10 @@ import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceFragment; +import org.chromium.base.ApplicationStatus; +import org.chromium.base.CommandLine; import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.PasswordUIView; import org.chromium.chrome.browser.autofill.PersonalDataManager; import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; @@ -103,8 +106,14 @@ public class MainPreferences extends PreferenceFragment implements SignInStateOb }); mSignInPreference.setEnabled(true); + // TODO(dfalcantara): Delete this preference entirely. https://crbug.com/582539 Preference documentMode = findPreference(PREF_DOCUMENT_MODE); - if (FeatureUtilities.isDocumentModeEligible(getActivity())) { + boolean showDocumentToggle = FeatureUtilities.isDocumentModeEligible(getActivity()); + if (CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABLE_FORCED_MIGRATION)) { + showDocumentToggle &= + FeatureUtilities.isDocumentMode(ApplicationStatus.getApplicationContext()); + } + if (showDocumentToggle) { setOnOffSummary(documentMode, !DocumentModeManager.getInstance(getActivity()).isOptedOutOfDocumentMode()); } else { diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassin.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassin.java index 89936ef..bc7e0ba 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassin.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassin.java @@ -12,6 +12,7 @@ import android.os.Build; import android.util.Pair; import org.chromium.base.ApplicationStatus; +import org.chromium.base.CommandLine; import org.chromium.base.FileUtils; import org.chromium.base.Log; import org.chromium.base.ObserverList; @@ -19,6 +20,7 @@ import org.chromium.base.StreamUtil; import org.chromium.base.ThreadUtils; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.browser.ChromeApplication; +import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.TabState; import org.chromium.chrome.browser.document.DocumentActivity; import org.chromium.chrome.browser.document.DocumentUtils; @@ -42,6 +44,8 @@ import java.nio.channels.FileChannel; import java.util.HashSet; import java.util.Set; +import javax.annotation.Nullable; + /** * Divorces Chrome's tabs from Android's Overview menu. Assumes native libraries are unavailable. * @@ -52,7 +56,6 @@ import java.util.Set; * into the tabbed mode directory. Incognito tabs are silently dropped, as with the previous * migration pathway. * - * TODO(dfalcantara): Check what happens if a user last viewed an Incognito tab. * TODO(dfalcantara): Check what happens on other launchers. * * Once all TabState files are copied, a TabModel metadata file is written out for the tabbed @@ -64,7 +67,6 @@ import java.util.Set; * DocumentActivity tasks in Android's Recents are removed, TabState files in the document mode * directory are deleted, and document mode preferences are cleared. * - * TODO(dfalcantara): Clean up the incognito notification, if possible. * TODO(dfalcantara): Add histograms for tracking migration progress. * * TODO(dfalcantara): Potential pitfalls that need to be accounted for: @@ -105,7 +107,7 @@ public class DocumentModeAssassin { static final int STAGE_CHANGE_SETTINGS_STARTED = 6; static final int STAGE_CHANGE_SETTINGS_DONE = 7; static final int STAGE_DELETION_STARTED = 8; - static final int STAGE_DONE = 9; + public static final int STAGE_DONE = 9; private static final String TAG = "DocumentModeAssassin"; @@ -137,7 +139,8 @@ public class DocumentModeAssassin { /** Returns whether or not a migration to tabbed mode from document mode is necessary. */ public static boolean isMigrationNecessary() { - return FeatureUtilities.isDocumentMode(ApplicationStatus.getApplicationContext()); + return CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABLE_FORCED_MIGRATION) + && FeatureUtilities.isDocumentMode(ApplicationStatus.getApplicationContext()); } /** Migrates the user from document mode to tabbed mode if necessary. */ @@ -165,18 +168,39 @@ public class DocumentModeAssassin { * * TODO(dfalcantara): Prevent migrating chrome:// pages? * - * @param selectedTabId ID of the last viewed non-Incognito tab. - * @param documentDirectory File pointing at the DocumentTabModel TabState file directory. - * @param tabbedDirectory File pointing at tabbed mode TabState file directory. + * @param selectedTabId ID of the last viewed non-Incognito tab. + * @param context Context to use when accessing directories. + * @param documentDirectoryOverride Overrides the default location for where document mode's + * TabState files are expected to be. + * @param tabbedDirectoryOverride Overrides the default location for where tabbed mode's + * TabState files are expected to be. */ - void copyTabStateFiles( - final int selectedTabId, final File documentDirectory, final File tabbedDirectory) { + void copyTabStateFiles(final int selectedTabId, final Context context, + @Nullable final File documentDirectoryOverride, + @Nullable final File tabbedDirectoryOverride) { ThreadUtils.assertOnUiThread(); if (!setStage(STAGE_INITIALIZED, STAGE_COPY_TAB_STATES_STARTED)) return; new AsyncTask() { + private DocumentTabModelImpl mNormalTabModel; + + @Override + protected void onPreExecute() { + if (documentDirectoryOverride == null) { + mNormalTabModel = (DocumentTabModelImpl) + ChromeApplication.getDocumentTabModelSelector().getModel(false); + } + } + @Override protected Void doInBackground(Void... params) { + File documentDirectory = documentDirectoryOverride == null + ? mNormalTabModel.getStorageDelegate().getStateDirectory() + : documentDirectoryOverride; + File tabbedDirectory = tabbedDirectoryOverride == null + ? TabPersistentStore.getStateDirectory(context, TAB_MODEL_INDEX) + : tabbedDirectoryOverride; + Log.d(TAG, "Copying TabState files from document to tabbed mode directory."); assert mMigratedTabIds.size() == 0; @@ -186,11 +210,12 @@ public class DocumentModeAssassin { // before all the other ones to mitigate storage issues for devices with limited // available storage. if (selectedTabId != Tab.INVALID_TAB_ID) { - copyTabStateFilesInternal(allTabStates, selectedTabId, true); + copyTabStateFilesInternal( + allTabStates, tabbedDirectory, selectedTabId, true); } // Copy over everything else. - copyTabStateFilesInternal(allTabStates, selectedTabId, false); + copyTabStateFilesInternal(allTabStates, tabbedDirectory, selectedTabId, false); } return null; } @@ -205,12 +230,13 @@ public class DocumentModeAssassin { * Copies the files from the document mode directory to the tabbed mode directory. * * @param allTabStates Listing of all files in the document mode directory. + * @param tabbedDirectory Directory for the tabbed mode files. * @param selectedTabId ID of the non-Incognito tab the user last viewed. May be * {@link Tab#INVALID_TAB_ID} if the ID is unknown. * @param copyOnlySelectedTab Copy only the TabState file for the selectedTabId. */ - private void copyTabStateFilesInternal( - File[] allTabStates, int selectedTabId, boolean copyOnlySelectedTab) { + private void copyTabStateFilesInternal(File[] allTabStates, File tabbedDirectory, + int selectedTabId, boolean copyOnlySelectedTab) { assert !ThreadUtils.runningOnUiThread(); for (int i = 0; i < allTabStates.length; i++) { // Trawl the directory for non-Incognito TabState files. @@ -281,12 +307,15 @@ public class DocumentModeAssassin { * means that the user loses some navigation history, but it's not a case document mode would * have been able to recover from anyway because the TabState stores the URL data. * - * @param tabbedDirectory Directory containing all of the main TabModel's files. - * @param normalTabModel DocumentTabModel containing information about non-Incognito tabs. - * @param migratedTabIds IDs of Tabs whose TabState files were copied successfully. + * @param normalTabModel DocumentTabModel containing info about non-Incognito tabs. + * @param migratedTabIds IDs of Tabs whose TabState files were copied successfully. + * @param context Context to access Files from. + * @param tabbedDirectoryOverride Overrides the default location for where tabbed mode's + * TabState files are expected to be. */ - void writeTabModelMetadata(final File tabbedDirectory, final DocumentTabModel normalTabModel, - final Set migratedTabIds) { + void writeTabModelMetadata(final DocumentTabModel normalTabModel, + final Set migratedTabIds, final Context context, + @Nullable final File tabbedDirectoryOverride) { ThreadUtils.assertOnUiThread(); if (!setStage(STAGE_COPY_TAB_STATES_DONE, STAGE_WRITE_TABMODEL_METADATA_STARTED)) return; @@ -329,6 +358,9 @@ public class DocumentModeAssassin { @Override protected Boolean doInBackground(Void... params) { if (mSerializedMetadata != null) { + File tabbedDirectory = tabbedDirectoryOverride == null + ? TabPersistentStore.getStateDirectory(context, TAB_MODEL_INDEX) + : tabbedDirectoryOverride; TabPersistentStore.saveListToFile(tabbedDirectory, mSerializedMetadata); return true; } else { @@ -440,22 +472,14 @@ public class DocumentModeAssassin { Context context = ApplicationStatus.getApplicationContext(); if (newStage == STAGE_INITIALIZED) { Log.d(TAG, "Migrating user into tabbed mode."); - DocumentTabModelSelector selector = ChromeApplication.getDocumentTabModelSelector(); - DocumentTabModelImpl normalTabModel = - (DocumentTabModelImpl) selector.getModel(false); int selectedTabId = DocumentUtils.getLastShownTabIdFromPrefs(context, false); - - File documentDirectory = normalTabModel.getStorageDelegate().getStateDirectory(); - File tabbedDirectory = TabPersistentStore.getStateDirectory(context, TAB_MODEL_INDEX); - copyTabStateFiles(selectedTabId, documentDirectory, tabbedDirectory); + copyTabStateFiles(selectedTabId, context, null, null); } else if (newStage == STAGE_COPY_TAB_STATES_DONE) { Log.d(TAG, "Writing tabbed mode metadata file."); DocumentTabModelSelector selector = ChromeApplication.getDocumentTabModelSelector(); DocumentTabModelImpl normalTabModel = (DocumentTabModelImpl) selector.getModel(false); - File tabbedDirectory = - TabPersistentStore.getStateDirectory(context, TAB_MODEL_INDEX); - writeTabModelMetadata(tabbedDirectory, normalTabModel, mMigratedTabIds); + writeTabModelMetadata(normalTabModel, mMigratedTabIds, context, null); } else if (newStage == STAGE_WRITE_TABMODEL_METADATA_DONE) { Log.d(TAG, "Changing user preference."); changePreferences(context); @@ -471,6 +495,7 @@ public class DocumentModeAssassin { */ @VisibleForTesting public int getStage() { + ThreadUtils.assertOnUiThread(); return mStage; } diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java index ff8ccad..a31f2b4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java @@ -11,6 +11,8 @@ import android.net.Uri; import android.text.TextUtils; import org.chromium.base.ApplicationStatus; +import org.chromium.base.CommandLine; +import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.TabState; import org.chromium.chrome.browser.UrlConstants; @@ -177,6 +179,6 @@ public class TabDelegate extends TabCreator { * @return Whether the TabDelegate is allowed to directly launch a DocumentActivity. */ protected boolean isAllowedToLaunchDocumentActivity(Context context) { - return true; + return !CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABLE_FORCED_MIGRATION); } } \ No newline at end of file diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index 95ef7e1..1e47a1f 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni @@ -50,6 +50,7 @@ chrome_java_sources = [ "java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java", "java/src/org/chromium/chrome/browser/TabState.java", "java/src/org/chromium/chrome/browser/TtsPlatformImpl.java", + "java/src/org/chromium/chrome/browser/UpgradeActivity.java", "java/src/org/chromium/chrome/browser/UrlConstants.java", "java/src/org/chromium/chrome/browser/WarmupManager.java", "java/src/org/chromium/chrome/browser/WebContentsFactory.java", diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java index 0e3566d..bceb753 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java @@ -210,12 +210,13 @@ public class DocumentModeAssassinTest extends NativeLibraryTestBase { int numFilesBefore = tabbedModeFilesBefore.length; assertEquals(0, writeStartedCallback.getCallCount()); assertEquals(0, writeDoneCallback.getCallCount()); + final Context context = getInstrumentation().getTargetContext(); ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { assassin.addObserver(observer); - assassin.writeTabModelMetadata( - mTabbedModeDirectory.getDataDirectory(), testTabModel, migratedTabIds); + assassin.writeTabModelMetadata(testTabModel, migratedTabIds, context, + mTabbedModeDirectory.getDataDirectory()); } }); @@ -231,7 +232,6 @@ public class DocumentModeAssassinTest extends NativeLibraryTestBase { loadNativeLibraryAndInitBrowserProcess(); TabPersistentStore.setBaseStateDirectory(mTabbedModeDirectory.getBaseDirectory()); - Context context = getInstrumentation().getTargetContext(); TestTabModelSelector selector = new TestTabModelSelector(context); TabPersistentStore store = selector.mTabPersistentStore; MockTabPersistentStoreObserver mockObserver = selector.mTabPersistentStoreObserver; @@ -329,7 +329,8 @@ public class DocumentModeAssassinTest extends NativeLibraryTestBase { assertEquals(0, copyStartedCallback.getCallCount()); assertEquals(0, copyDoneCallback.getCallCount()); assertEquals(0, copyCallback.getCallCount()); - assassin.copyTabStateFiles(selectedTabId, mDocumentModeDirectory.getDataDirectory(), + assassin.copyTabStateFiles(selectedTabId, getInstrumentation().getTargetContext(), + mDocumentModeDirectory.getDataDirectory(), mTabbedModeDirectory.getDataDirectory()); } }); diff --git a/ui/android/java/strings/android_ui_strings.grd b/ui/android/java/strings/android_ui_strings.grd index 1ce51bc..5d69aba 100644 --- a/ui/android/java/strings/android_ui_strings.grd +++ b/ui/android/java/strings/android_ui_strings.grd @@ -234,6 +234,11 @@ Suggestions available + + + + Updating Chrome... + -- cgit v1.1