| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.chrome.browser.document; | |
| 6 | |
| 7 import android.annotation.TargetApi; | |
| 8 import android.app.Activity; | |
| 9 import android.app.ActivityManager; | |
| 10 import android.app.ActivityManager.AppTask; | |
| 11 import android.app.ActivityManager.TaskDescription; | |
| 12 import android.content.Context; | |
| 13 import android.content.Intent; | |
| 14 import android.graphics.Bitmap; | |
| 15 import android.graphics.Bitmap.Config; | |
| 16 import android.graphics.Canvas; | |
| 17 import android.graphics.Color; | |
| 18 import android.os.Build; | |
| 19 import android.os.StrictMode; | |
| 20 import android.text.TextUtils; | |
| 21 import android.util.Pair; | |
| 22 import android.util.SparseArray; | |
| 23 | |
| 24 import org.chromium.base.ApiCompatibilityUtils; | |
| 25 import org.chromium.base.ContextUtils; | |
| 26 import org.chromium.base.ImportantFileWriterAndroid; | |
| 27 import org.chromium.base.Log; | |
| 28 import org.chromium.chrome.R; | |
| 29 import org.chromium.chrome.browser.ActivityTaskDescriptionIconGenerator; | |
| 30 import org.chromium.chrome.browser.ApplicationLifetime; | |
| 31 import org.chromium.chrome.browser.ChromeApplication; | |
| 32 import org.chromium.chrome.browser.IntentHandler; | |
| 33 import org.chromium.chrome.browser.TabState; | |
| 34 import org.chromium.chrome.browser.UrlConstants; | |
| 35 import org.chromium.chrome.browser.compositor.layouts.content.ContentOffsetProvi
der; | |
| 36 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; | |
| 37 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager.
DecompressThumbnailCallback; | |
| 38 import org.chromium.chrome.browser.device.DeviceClassManager; | |
| 39 import org.chromium.chrome.browser.favicon.FaviconHelper; | |
| 40 import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback; | |
| 41 import org.chromium.chrome.browser.ntp.NativePageFactory; | |
| 42 import org.chromium.chrome.browser.preferences.ChromePreferenceManager; | |
| 43 import org.chromium.chrome.browser.profiles.Profile; | |
| 44 import org.chromium.chrome.browser.tab.Tab; | |
| 45 import org.chromium.chrome.browser.tab.TabIdManager; | |
| 46 import org.chromium.chrome.browser.tabmodel.TabCreatorManager; | |
| 47 import org.chromium.chrome.browser.tabmodel.TabPersistentStore; | |
| 48 import org.chromium.chrome.browser.tabmodel.TabPersistentStore.OnTabStateReadCal
lback; | |
| 49 import org.chromium.chrome.browser.tabmodel.document.ActivityDelegate; | |
| 50 import org.chromium.chrome.browser.tabmodel.document.ActivityDelegateImpl; | |
| 51 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModel; | |
| 52 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModel.Entry; | |
| 53 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModel.Initializa
tionObserver; | |
| 54 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelImpl; | |
| 55 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelSelector; | |
| 56 import org.chromium.chrome.browser.tabmodel.document.OffTheRecordDocumentTabMode
l; | |
| 57 import org.chromium.chrome.browser.tabmodel.document.StorageDelegate; | |
| 58 import org.chromium.chrome.browser.tabmodel.document.TabDelegate; | |
| 59 import org.chromium.chrome.browser.util.UrlUtilities; | |
| 60 | |
| 61 import java.io.File; | |
| 62 import java.io.IOException; | |
| 63 import java.util.ArrayList; | |
| 64 import java.util.List; | |
| 65 import java.util.concurrent.atomic.AtomicInteger; | |
| 66 | |
| 67 /** | |
| 68 * The class that carries out migration of tab states from/to document mode. | |
| 69 */ | |
| 70 @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
| 71 public class DocumentMigrationHelper { | |
| 72 private static final String TAG = "DocumentMigration"; | |
| 73 | |
| 74 public static final int FINALIZE_MODE_NO_ACTION = 0; | |
| 75 public static final int FINALIZE_MODE_FINISH_ACTIVITY = 1; | |
| 76 public static final int FINALIZE_MODE_RESTART_APP = 2; | |
| 77 | |
| 78 private static final List<MigrationImageCallback> sMigrationCallbacks = | |
| 79 new ArrayList<MigrationImageCallback>(); | |
| 80 | |
| 81 private static class MigrationTabStateReadCallback implements OnTabStateRead
Callback { | |
| 82 private int mSelectedTabId = Tab.INVALID_TAB_ID; | |
| 83 | |
| 84 @Override | |
| 85 public void onDetailsRead(int index, int id, String url, Boolean isIncog
nito, | |
| 86 boolean isStandardActiveIndex, boolean isIncognitoActiveIndex) { | |
| 87 TabIdManager.getInstance().incrementIdCounterTo(id + 1); | |
| 88 if (!isStandardActiveIndex) return; | |
| 89 // If the current tab read is the active standard tab, set the last
used | |
| 90 // tab pref with the id, so that when document mode starts we show t
hat | |
| 91 // tab first. | |
| 92 mSelectedTabId = id; | |
| 93 } | |
| 94 | |
| 95 public int getSelectedTabId() { | |
| 96 return mSelectedTabId; | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 /** | |
| 101 * Stores a list of "tasks" that are meant to be returned by the ActivityMan
ager for migration. | |
| 102 * The tasks are inserted manually during the migration from classic mode to
document mode. | |
| 103 */ | |
| 104 private static class MigrationActivityDelegate extends ActivityDelegateImpl
{ | |
| 105 private final List<Entry> mEntries; | |
| 106 private final int mSelectedTabId; | |
| 107 | |
| 108 private MigrationActivityDelegate(List<Entry> entries, int selectedTabId
) { | |
| 109 super(DocumentActivity.class, IncognitoDocumentActivity.class); | |
| 110 mEntries = entries; | |
| 111 mSelectedTabId = selectedTabId; | |
| 112 } | |
| 113 | |
| 114 public int getSelectedTabId() { | |
| 115 return mSelectedTabId; | |
| 116 } | |
| 117 | |
| 118 @Override | |
| 119 public boolean isValidActivity(boolean isIncognito, Intent intent) { | |
| 120 return true; | |
| 121 } | |
| 122 | |
| 123 @Override | |
| 124 public List<Entry> getTasksFromRecents(boolean isIncognito) { | |
| 125 // We need to have our own list here, since these entries have not a
ctually been | |
| 126 // created in Recents yet. | |
| 127 return mEntries; | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 private static class MigrationTabCreatorManager implements TabCreatorManager
{ | |
| 132 TabDelegate mRegularTabCreator = new TabDelegate(false); | |
| 133 TabDelegate mIncognitoTabCreator = new TabDelegate(true); | |
| 134 | |
| 135 @Override | |
| 136 public TabDelegate getTabCreator(boolean incognito) { | |
| 137 return incognito ? mIncognitoTabCreator : mRegularTabCreator; | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 private static class MigrationTabModel extends DocumentTabModelImpl { | |
| 142 private final SparseArray<String> mTitleList; | |
| 143 | |
| 144 /** | |
| 145 * Constucts a {@link DocumentTabModel} to be used for migration. | |
| 146 * @param activityDelegate The delegate that has the tabs to be migrated
. | |
| 147 * @param storageDelegate Delegate that interacts with the file system. | |
| 148 */ | |
| 149 MigrationTabModel(MigrationActivityDelegate activityDelegate, | |
| 150 StorageDelegate storageDelegate) { | |
| 151 super(activityDelegate, storageDelegate, new MigrationTabCreatorMana
ger(), false, | |
| 152 Tab.INVALID_TAB_ID, ContextUtils.getApplicationContext()); | |
| 153 startTabStateLoad(); | |
| 154 mTitleList = new SparseArray<String>(); | |
| 155 setLastShownId(activityDelegate.getSelectedTabId()); | |
| 156 } | |
| 157 | |
| 158 /** | |
| 159 * Returns the display title for the Document with the given ID. | |
| 160 * @param tabId The ID for the document to return the url for. | |
| 161 * @return The display title for the entry if it was found, null otherwi
se. | |
| 162 */ | |
| 163 public String getTitleForDocument(int tabId) { | |
| 164 String title = mTitleList.get(tabId); | |
| 165 return TextUtils.isEmpty(title) ? "" : title; | |
| 166 } | |
| 167 | |
| 168 @Override | |
| 169 protected boolean shouldStartDeserialization(int currentState) { | |
| 170 return currentState == STATE_LOAD_TAB_STATE_BG_END; | |
| 171 } | |
| 172 | |
| 173 @Override | |
| 174 protected void updateEntryInfoFromTabState(Entry entry, TabState tabStat
e) { | |
| 175 super.updateEntryInfoFromTabState(entry, tabState); | |
| 176 mTitleList.put(entry.tabId, tabState.getDisplayTitleFromState()); | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 private static class MigrationImageCallback | |
| 181 implements FaviconImageCallback, DecompressThumbnailCallback { | |
| 182 private final AtomicInteger mCompletedCount; | |
| 183 private final Activity mActivity; | |
| 184 private final TabContentManager mTabContentManager; | |
| 185 private final MigrationTabModel mTabModel; | |
| 186 private final int mTabId; | |
| 187 private final String mUrl; | |
| 188 private final String mTitle; | |
| 189 private final int mFinalizeMode; | |
| 190 | |
| 191 private Bitmap mIcon; | |
| 192 | |
| 193 public MigrationImageCallback(final AtomicInteger completedCount, final
Activity activity, | |
| 194 final TabContentManager tabContentManager, final MigrationTabMod
el tabModel, | |
| 195 final int tabId, final String url, final String title, final int
finalizeMode) { | |
| 196 mCompletedCount = completedCount; | |
| 197 mActivity = activity; | |
| 198 mTabContentManager = tabContentManager; | |
| 199 mTabModel = tabModel; | |
| 200 mTabId = tabId; | |
| 201 mUrl = url; | |
| 202 mTitle = title; | |
| 203 mFinalizeMode = finalizeMode; | |
| 204 } | |
| 205 | |
| 206 @Override | |
| 207 public void onFaviconAvailable(final Bitmap favicon, String iconUrl) { | |
| 208 mIcon = new ActivityTaskDescriptionIconGenerator(mActivity).getBitma
p(mUrl, favicon); | |
| 209 mTabContentManager.getThumbnailForId(mTabId, this); | |
| 210 } | |
| 211 | |
| 212 @Override | |
| 213 public void onFinishGetBitmap(Bitmap bitmap) { | |
| 214 // Even if the favicon or thumbnail are null, we still add the AppTa
sk: the framework | |
| 215 // handles null favicons and addAppTask() below handles null thumbna
ils. | |
| 216 if (!NativePageFactory.isNativePageUrl(mUrl, false) | |
| 217 && !mUrl.startsWith(UrlConstants.CHROME_SCHEME)) { | |
| 218 addAppTask(mActivity, mTabId, mTabModel.getTabStateForDocument(m
TabId), mUrl, | |
| 219 mTitle, mIcon, bitmap); | |
| 220 } | |
| 221 | |
| 222 if (mCompletedCount.incrementAndGet() == mTabModel.getCount()) { | |
| 223 mTabContentManager.destroy(); | |
| 224 finalizeMigration(mActivity, mFinalizeMode); | |
| 225 } | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 /** | |
| 230 * Migrates all tab state to classic mode and creates a tab model file using
the current | |
| 231 * {@link DocumentTabModel} instances. | |
| 232 * @param activity The activity to be finished after migration if necessary. | |
| 233 * @param finalizeMode The mode in which the migration should be finalized. | |
| 234 */ | |
| 235 public static void migrateTabsFromDocumentToClassic(final Activity activity, | |
| 236 int finalizeMode) { | |
| 237 Context context = ContextUtils.getApplicationContext(); | |
| 238 // Before migration we remove all incognito tabs and also remove the | |
| 239 // tabs that can not be reached through the {@link DocumentTabModel} ins
tances. | |
| 240 List<Integer> tabIdsToRemove = new ArrayList<Integer>(); | |
| 241 | |
| 242 DocumentTabModelImpl normalTabModel = (DocumentTabModelImpl) | |
| 243 ChromeApplication.getDocumentTabModelSelector().getModel(false); | |
| 244 OffTheRecordDocumentTabModel incognitoTabModel = (OffTheRecordDocumentTa
bModel) | |
| 245 ChromeApplication.getDocumentTabModelSelector().getModel(true); | |
| 246 | |
| 247 // TODO(yusufo): Clean up this logic. | |
| 248 for (int i = 0; i < incognitoTabModel.getCount(); i++) { | |
| 249 tabIdsToRemove.add(incognitoTabModel.getTabAt(i).getId()); | |
| 250 } | |
| 251 | |
| 252 ActivityManager am = | |
| 253 (ActivityManager) context.getSystemService(Activity.ACTIVITY_SER
VICE); | |
| 254 List<AppTask> taskList = am.getAppTasks(); | |
| 255 for (int i = 0; i < taskList.size(); i++) { | |
| 256 Intent intent = DocumentUtils.getBaseIntentFromTask(taskList.get(i))
; | |
| 257 int id = ActivityDelegate.getTabIdFromIntent(intent); | |
| 258 if (id == Tab.INVALID_TAB_ID) continue; | |
| 259 if (tabIdsToRemove.contains(id)) taskList.get(i).finishAndRemoveTask
(); | |
| 260 } | |
| 261 incognitoTabModel.updateRecentlyClosed(); | |
| 262 | |
| 263 File migratedFolder = TabPersistentStore.getStateDirectory(context, 0); | |
| 264 String tabStatefileName = new File(migratedFolder, | |
| 265 TabPersistentStore.SAVED_STATE_FILE).getAbsolutePath(); | |
| 266 | |
| 267 // All the TabStates (incognito or not) live in the same directory. | |
| 268 File[] allTabs = normalTabModel.getStorageDelegate().getStateDirectory()
.listFiles(); | |
| 269 try { | |
| 270 if (allTabs != null) { | |
| 271 for (int i = 0; i < allTabs.length; i++) { | |
| 272 String fileName = allTabs[i].getName(); | |
| 273 Pair<Integer, Boolean> tabInfo = TabState.parseInfoFromFilen
ame(fileName); | |
| 274 if (tabInfo == null) continue; | |
| 275 int tabId = tabInfo.first; | |
| 276 | |
| 277 // Also remove the tab state file for the closed tabs. | |
| 278 boolean success; | |
| 279 if (!tabIdsToRemove.contains(tabId)) { | |
| 280 success = allTabs[i].renameTo(new File(migratedFolder, f
ileName)); | |
| 281 } else { | |
| 282 success = allTabs[i].delete(); | |
| 283 } | |
| 284 | |
| 285 if (!success) Log.e(TAG, "Failed to move/delete file for tab
ID: " + tabId); | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 if (normalTabModel.getCount() != 0) { | |
| 290 byte[] listData; | |
| 291 listData = TabPersistentStore.serializeTabModelSelector( | |
| 292 ChromeApplication.getDocumentTabModelSelector(), null); | |
| 293 ImportantFileWriterAndroid.writeFileAtomically(tabStatefileName,
listData); | |
| 294 } | |
| 295 } catch (IOException e) { | |
| 296 Log.e(TAG , "IO exception during tab migration, tab state might not
restore correctly"); | |
| 297 } | |
| 298 finalizeMigration(activity, finalizeMode); | |
| 299 } | |
| 300 | |
| 301 /** | |
| 302 * Migrates all tab state to document mode and creates tasks for each curren
tly open tab. | |
| 303 * @param activity Activity to be used while launching the tasks. | |
| 304 * @param finalizeMode The mode in which the migration should be finalized. | |
| 305 */ | |
| 306 public static void migrateTabsFromClassicToDocument( | |
| 307 final Activity activity, final int finalizeMode) { | |
| 308 StorageDelegate storageDelegate = new StorageDelegate(); | |
| 309 MigrationActivityDelegate activityDelegate = | |
| 310 createActivityDelegateWithTabsToMigrate(storageDelegate, activit
y); | |
| 311 final MigrationTabModel normalTabModel = | |
| 312 new MigrationTabModel(activityDelegate, storageDelegate); | |
| 313 | |
| 314 InitializationObserver observer = new InitializationObserver(normalTabMo
del) { | |
| 315 @Override | |
| 316 protected void runImmediately() { | |
| 317 addAppTasksFromFiles(activity, normalTabModel, finalizeMode); | |
| 318 } | |
| 319 | |
| 320 @Override | |
| 321 public boolean isSatisfied(int currentState) { | |
| 322 return currentState == DocumentTabModelImpl.STATE_DESERIALIZE_EN
D; | |
| 323 } | |
| 324 | |
| 325 @Override | |
| 326 public boolean isCanceled() { | |
| 327 return false; | |
| 328 } | |
| 329 }; | |
| 330 | |
| 331 observer.runWhenReady(); | |
| 332 } | |
| 333 | |
| 334 /** | |
| 335 * Migrate tabs saved in classic mode to document mode for an upgrade. This
doesn't restart | |
| 336 * the app process but only finishes the {@link ChromeLauncherActivity} it w
as being called | |
| 337 * with. | |
| 338 * @param activity The activity to use for carrying out and finalizing the m
igration. | |
| 339 * @param finalizeMode The mode in which the migration should be finalized. | |
| 340 * @return Whether any tabs will be migrated. | |
| 341 */ | |
| 342 public static boolean migrateTabsToDocumentForUpgrade(Activity activity, | |
| 343 int finalizeMode) { | |
| 344 // Allow StrictMode violations here as state file read/write is on the c
ritical path. This | |
| 345 // is also a one-time migration, so optimizing further does not improve
daily experience. | |
| 346 // See http://crbug.com/493157 for more context. | |
| 347 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); | |
| 348 StrictMode.allowThreadDiskWrites(); | |
| 349 try { | |
| 350 ChromePreferenceManager.getInstance(activity).setAttemptedMigrationO
nUpgrade(); | |
| 351 | |
| 352 File[] fileList = TabPersistentStore.getStateDirectory(activity, 0).
listFiles(); | |
| 353 if (fileList == null || fileList.length == 0 | |
| 354 || (fileList.length == 1 | |
| 355 && fileList[0].getName().equals(TabPersistentStore.SAVED_STA
TE_FILE))) { | |
| 356 return false; | |
| 357 } | |
| 358 | |
| 359 migrateTabsFromClassicToDocument(activity, finalizeMode); | |
| 360 return true; | |
| 361 } finally { | |
| 362 StrictMode.setThreadPolicy(oldPolicy); | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 private static void finalizeMigration(Activity activity, final int mode) { | |
| 367 sMigrationCallbacks.clear(); | |
| 368 | |
| 369 switch(mode) { | |
| 370 case FINALIZE_MODE_NO_ACTION: | |
| 371 return; | |
| 372 case FINALIZE_MODE_FINISH_ACTIVITY: | |
| 373 activity.finishAndRemoveTask(); | |
| 374 return; | |
| 375 case FINALIZE_MODE_RESTART_APP: | |
| 376 ApplicationLifetime.terminate(true); | |
| 377 return; | |
| 378 default: | |
| 379 assert false; | |
| 380 } | |
| 381 } | |
| 382 | |
| 383 private static MigrationActivityDelegate createActivityDelegateWithTabsToMig
rate( | |
| 384 StorageDelegate storageDelegate, Activity activity) { | |
| 385 File migratedFolder = storageDelegate.getStateDirectory(); | |
| 386 if (!migratedFolder.exists() && !migratedFolder.mkdir()) { | |
| 387 Log.e(TAG, "Failed to create folder: " + migratedFolder.getAbsoluteP
ath()); | |
| 388 } | |
| 389 | |
| 390 // Create maps for all tabs that will be used during TabModel initializa
tion. | |
| 391 final List<Entry> normalEntryMap = new ArrayList<Entry>(); | |
| 392 | |
| 393 File currentFolder = null; | |
| 394 MigrationTabStateReadCallback callback = new MigrationTabStateReadCallba
ck(); | |
| 395 for (int currentSelectorIndex = 0; | |
| 396 (currentFolder = getNextNonEmptyFolder(activity, currentSelector
Index)) != null; | |
| 397 ++currentSelectorIndex) { | |
| 398 File[] allTabs = TabPersistentStore | |
| 399 .getStateDirectory(activity, currentSelectorIndex).listFiles
(); | |
| 400 try { | |
| 401 TabPersistentStore.readSavedStateFile(currentFolder, callback); | |
| 402 } catch (IOException e) { | |
| 403 Log.e(TAG, "IO Exception while trying to get the last used tab i
d"); | |
| 404 } | |
| 405 | |
| 406 if (allTabs != null) { | |
| 407 for (int i = 0; i < allTabs.length; i++) { | |
| 408 // Move tab state file to the document side folder. | |
| 409 String fileName = allTabs[i].getName(); | |
| 410 Pair<Integer, Boolean> tabInfo = TabState.parseInfoFromFilen
ame(fileName); | |
| 411 if (tabInfo == null) continue; | |
| 412 | |
| 413 boolean success; | |
| 414 if (tabInfo.second) { | |
| 415 success = allTabs[i].delete(); | |
| 416 } else { | |
| 417 success = allTabs[i].renameTo(new File(migratedFolder, f
ileName)); | |
| 418 normalEntryMap.add(new Entry(tabInfo.first, UrlConstants
.NTP_URL)); | |
| 419 } | |
| 420 | |
| 421 if (!success) Log.e(TAG, "Failed to move/delete file: " + fi
leName); | |
| 422 } | |
| 423 } | |
| 424 } | |
| 425 | |
| 426 return new MigrationActivityDelegate(normalEntryMap, callback.getSelecte
dTabId()); | |
| 427 } | |
| 428 | |
| 429 private static File getNextNonEmptyFolder(Activity activity, int currentSele
ctorIndex) { | |
| 430 File folder = TabPersistentStore.getStateDirectory(activity, currentSele
ctorIndex); | |
| 431 File[] files = folder.listFiles(); | |
| 432 return (files == null || files.length == 0) ? null : folder; | |
| 433 } | |
| 434 | |
| 435 private static void addAppTasksFromFiles(final Activity activity, | |
| 436 final MigrationTabModel tabModel, final int finalizeMode) { | |
| 437 assert sMigrationCallbacks.size() == 0; | |
| 438 | |
| 439 if (tabModel.getCount() == 0) { | |
| 440 finalizeMigration(activity, finalizeMode); | |
| 441 return; | |
| 442 } | |
| 443 | |
| 444 final AtomicInteger completedCount = new AtomicInteger(); | |
| 445 final TabContentManager contentManager = | |
| 446 new TabContentManager(activity, new ContentOffsetProvider() { | |
| 447 @Override | |
| 448 public int getOverlayTranslateY() { | |
| 449 return 0; | |
| 450 } | |
| 451 }, DeviceClassManager.enableSnapshots()); | |
| 452 FaviconHelper faviconHelper = new FaviconHelper(); | |
| 453 for (int i = 0; i < tabModel.getCount(); i++) { | |
| 454 final int tabId = tabModel.getTabAt(i).getId(); | |
| 455 String currentUrl = tabModel.getCurrentUrlForDocument(tabId); | |
| 456 String currentTitle = tabModel.getTitleForDocument(tabId); | |
| 457 | |
| 458 // Use placeholders if we can't find anything for url and title. | |
| 459 if (TextUtils.isEmpty(currentUrl)) currentUrl = UrlConstants.NTP_URL
; | |
| 460 if (TextUtils.isEmpty(currentTitle) | |
| 461 && !NativePageFactory.isNativePageUrl(currentUrl, false)) { | |
| 462 currentTitle = UrlUtilities.getDomainAndRegistry(currentUrl, fal
se); | |
| 463 } | |
| 464 final String url = currentUrl; | |
| 465 final String title = currentTitle; | |
| 466 | |
| 467 MigrationImageCallback imageCallback = new MigrationImageCallback(co
mpletedCount, | |
| 468 activity, contentManager, tabModel, tabId, url, title, final
izeMode); | |
| 469 faviconHelper.getLocalFaviconImageForURL( | |
| 470 Profile.getLastUsedProfile().getOriginalProfile(), | |
| 471 url, 0, imageCallback); | |
| 472 sMigrationCallbacks.add(imageCallback); | |
| 473 } | |
| 474 } | |
| 475 | |
| 476 private static void addAppTask(Activity activity, int tabId, TabState tabSta
te, | |
| 477 String currentUrl, String title, Bitmap favicon, Bitmap bitmap) { | |
| 478 if (tabId == ActivityDelegate.getTabIdFromIntent(activity.getIntent()))
return; | |
| 479 // Create intent and taskDescription. | |
| 480 Intent intent = new Intent(Intent.ACTION_VIEW, | |
| 481 DocumentTabModelSelector.createDocumentDataString(tabId, current
Url)); | |
| 482 intent.setClassName(activity, ChromeLauncherActivity.getDocumentClassNam
e(false)); | |
| 483 intent.putExtra(IntentHandler.EXTRA_PRESERVE_TASK, true); | |
| 484 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); | |
| 485 ActivityManager am = | |
| 486 (ActivityManager) activity.getSystemService(Activity.ACTIVITY_SE
RVICE); | |
| 487 | |
| 488 Bitmap thumbnail = Bitmap.createBitmap(am.getAppTaskThumbnailSize().getW
idth(), | |
| 489 am.getAppTaskThumbnailSize().getHeight(), Config.ARGB_8888); | |
| 490 Canvas canvas = new Canvas(thumbnail); | |
| 491 if (bitmap == null) { | |
| 492 canvas.drawColor(Color.WHITE); | |
| 493 } else { | |
| 494 float scale = Math.max( | |
| 495 (float) thumbnail.getWidth() / bitmap.getWidth(), | |
| 496 (float) thumbnail.getHeight() / bitmap.getHeight()); | |
| 497 canvas.scale(scale, scale); | |
| 498 canvas.drawBitmap(bitmap, 0, 0, null); | |
| 499 } | |
| 500 TaskDescription taskDescription = new TaskDescription(title, favicon, | |
| 501 ApiCompatibilityUtils.getColor(activity.getResources(), | |
| 502 R.color.default_primary_color)); | |
| 503 am.addAppTask(activity, intent, taskDescription, thumbnail); | |
| 504 Entry entry = new Entry(tabId, tabState); | |
| 505 DocumentTabModelImpl tabModel = (DocumentTabModelImpl) ChromeApplication | |
| 506 .getDocumentTabModelSelector().getModel(false); | |
| 507 tabModel.addEntryForMigration(entry); | |
| 508 } | |
| 509 | |
| 510 | |
| 511 /** | |
| 512 * Migrates tabs with state to and from document mode. | |
| 513 * @param toDocumentMode Whether the user is opting out. If true the migrati
on is from Document | |
| 514 * to Classic mode. | |
| 515 * @param activity The activity to use for launching intent if needed. | |
| 516 * @param terminate Whether the application process should be terminated aft
er the migration. | |
| 517 */ | |
| 518 public static void migrateTabs(boolean toDocumentMode, final Activity activi
ty, | |
| 519 boolean terminate) { | |
| 520 // Allowing StrictMode violations as above. See http://crbug.com/493157
for context. | |
| 521 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); | |
| 522 StrictMode.allowThreadDiskWrites(); | |
| 523 try { | |
| 524 int terminateMode = | |
| 525 terminate ? FINALIZE_MODE_RESTART_APP : FINALIZE_MODE_FINISH
_ACTIVITY; | |
| 526 if (toDocumentMode) { | |
| 527 migrateTabsFromClassicToDocument(activity, terminateMode); | |
| 528 } else { | |
| 529 migrateTabsFromDocumentToClassic(activity, terminateMode); | |
| 530 } | |
| 531 } finally { | |
| 532 StrictMode.setThreadPolicy(oldPolicy); | |
| 533 } | |
| 534 } | |
| 535 } | |
| OLD | NEW |