diff options
14 files changed, 348 insertions, 44 deletions
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. <meta-data android:name="android.app.searchable" android:resource="@xml/searchable" /> </activity-alias> + + <!-- Upgrade related --> + <activity android:name="org.chromium.chrome.browser.UpgradeActivity" + android:excludeFromRecents="true" + android:theme="@style/MainTheme" + android:windowSoftInputMode="adjustResize" + android:taskAffinity="" + android:launchMode="singleInstance" + android:persistableMode="persistNever" + android:autoRemoveFromRecents="false" + android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize" + android:hardwareAccelerated="false"> + </activity> + + <!-- Document mode Activities. --> <activity android:name="org.chromium.chrome.browser.document.DocumentActivity" android:exported="false" android:theme="@style/MainTheme" @@ -228,6 +243,8 @@ by a child template that "extends" this file. <activity-alias android:name="com.google.android.apps.chrome.document.IncognitoDocumentActivity" android:targetActivity="org.chromium.chrome.browser.document.IncognitoDocumentActivity" android:exported="false"/> + + <!-- Custom Tabs --> <activity android:name="org.chromium.chrome.browser.customtabs.CustomTabActivity" android:theme="@style/MainTheme" android:exported="false" @@ -235,6 +252,8 @@ by a child template that "extends" this file. android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize" android:hardwareAccelerated="false"> </activity> + + <!-- ChromeTabbedActivity related --> <activity android:name="org.chromium.chrome.browser.ChromeTabbedActivity" android:theme="@style/MainTheme" android:exported="false" @@ -252,6 +271,7 @@ by a child template that "extends" this file. android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize" android:hardwareAccelerated="false"> </activity> + <activity android:name="org.chromium.chrome.browser.sync.ui.PassphraseActivity" android:theme="@style/MainTheme" android:autoRemoveFromRecents="true"> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<!-- Adapted from webapp_splash_screen_large.xml. + Actual layout pending UX review. --> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/webapp_splash_screen_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginBottom="@dimen/webapp_splash_offset" > + + <ImageView + android:id="@+id/logo_view" + android:layout_width="@dimen/signin_image_carousel_width" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:contentDescription="@null" + android:src="@drawable/fre_product_logo" /> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:layout_centerHorizontal="true" + android:layout_marginBottom="@dimen/webapp_splash_large_title_margin_bottom" + android:gravity="center" + android:orientation="horizontal" > + + <ProgressBar + android:layout_width="20dp" + android:layout_height="20dp" + android:layout_marginEnd="12dp" + android:layout_weight="0" /> + + <TextView + android:id="@+id/message_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="0" + android:text="@string/updating_chrome" + android:textSize="20sp" + android:textColor="@color/default_text_color" + android:gravity="center" + android:fontFamily="sans-serif" + android:maxLines="1" /> + + </LinearLayout> + +</RelativeLayout> 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<Void, Void, Void>() { + 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<Integer> migratedTabIds) { + void writeTabModelMetadata(final DocumentTabModel normalTabModel, + final Set<Integer> 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 @@ <message name="IDS_AUTOFILL_KEYBOARD_ACCESSORY_CONTENT_DESCRIPTION" desc="The text announced by the screen reader when the autofill suggestions are shown."> Suggestions available </message> + + <!-- Migration strings --> + <message name="IDS_UPDATING_CHROME" desc="String that indicates that Chrome is updating"> + Updating Chrome... + </message> </messages> </release> </grit> |