// Copyright 2014 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.tabmodel.document; import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.ActivityManager.RecentTaskInfo; import android.content.Context; import android.content.SharedPreferences; import android.os.Build; import org.chromium.base.ContextUtils; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.browser.document.DocumentUtils; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabIdManager; import org.chromium.chrome.browser.tabmodel.IncognitoTabModel.IncognitoTabModelDelegate; import org.chromium.chrome.browser.tabmodel.TabCreatorManager; import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; import org.chromium.chrome.browser.tabmodel.TabModelSelectorBase; import org.chromium.content_public.browser.LoadUrlParams; /** * Deprecated. Kept around only for migration. * * Stores DocumentTabModels for Chrome Activities running in Document-mode. * Also manages the transfer of data from one DocumentActivity to another, e.g. WebContents that are * created by one Activity but need to be loaded in another Tab. */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) public class DocumentTabModelSelector extends TabModelSelectorBase implements TabCreatorManager { public static final String PREF_PACKAGE = "com.google.android.apps.chrome.document"; public static final String PREF_IS_INCOGNITO_SELECTED = "is_incognito_selected"; /** * Overrides Delegates used by the DocumentTabModels. */ private static final Object STORAGE_DELEGATE_FOR_TESTS_LOCK = new Object(); private static StorageDelegate sStorageDelegateForTests; /** * ID of the Tab to prioritize when initializing the TabState. */ private static int sPrioritizedTabId = Tab.INVALID_TAB_ID; /** * Interacts with DocumentActivities. */ private final ActivityDelegate mActivityDelegate; /** * Interacts with the file system. */ private final StorageDelegate mStorageDelegate; /** * Creates new Tabs. */ private final TabDelegate mRegularTabDelegate; private final TabDelegate mIncognitoTabDelegate; /** * TabModel that keeps track of regular tabs. This is always not null. */ private final DocumentTabModel mRegularTabModel; /** * TabModel that keeps track of incognito tabs. This may be null if no incognito tabs exist. */ private final IncognitoDocumentTabModel mIncognitoTabModel; /** * If the TabModels haven't been initialized yet, prioritize the correct one to load the Tab. * @param prioritizedTabId ID of the tab to prioritize. */ public static void setPrioritizedTabId(int prioritizedTabId) { sPrioritizedTabId = prioritizedTabId; } public DocumentTabModelSelector(ActivityDelegate activityDelegate, StorageDelegate storageDelegate, TabDelegate regularTabDelegate, TabDelegate incognitoTabDelegate) { mActivityDelegate = activityDelegate; mStorageDelegate = sStorageDelegateForTests == null ? storageDelegate : sStorageDelegateForTests; mRegularTabDelegate = regularTabDelegate; mIncognitoTabDelegate = incognitoTabDelegate; final Context context = ContextUtils.getApplicationContext(); mRegularTabModel = new DocumentTabModelImpl( mActivityDelegate, mStorageDelegate, this, false, sPrioritizedTabId, context); mIncognitoTabModel = new IncognitoDocumentTabModel(new IncognitoTabModelDelegate() { @Override public TabModel createTabModel() { DocumentTabModel incognitoModel = new DocumentTabModelImpl(mActivityDelegate, mStorageDelegate, DocumentTabModelSelector.this, true, sPrioritizedTabId, context); return incognitoModel; } @Override public boolean doIncognitoTabsExist() { // TODO(dfalcantara): Devices in document mode do not trigger the TabWindowManager. // Revisit this when we have a Samsung L multi-instance device. return mIncognitoTabModel.getCount() > 0; } }, mActivityDelegate); initializeTabIdCounter(); // Re-select the previously selected TabModel. SharedPreferences prefs = context.getSharedPreferences(PREF_PACKAGE, Context.MODE_PRIVATE); boolean startIncognito = prefs.getBoolean(PREF_IS_INCOGNITO_SELECTED, false); initialize(startIncognito, mRegularTabModel, mIncognitoTabModel); } @Override public TabDelegate getTabCreator(boolean incognito) { return incognito ? mIncognitoTabDelegate : mRegularTabDelegate; } private void initializeTabIdCounter() { int biggestId = getLargestTaskIdFromRecents(); biggestId = getMaxTabId(mRegularTabModel, biggestId); biggestId = getMaxTabId(mIncognitoTabModel, biggestId); TabIdManager.getInstance().incrementIdCounterTo(biggestId + 1); } private int getMaxTabId(DocumentTabModel tabModel, int min) { int biggestId = min; int numTabs = tabModel.getCount(); for (int tabIndex = 0; tabIndex < numTabs; tabIndex++) { biggestId = Math.max(biggestId, tabModel.getTabAt(tabIndex).getId()); } return biggestId; } private int getLargestTaskIdFromRecents() { int biggestId = Tab.INVALID_TAB_ID; Context context = ContextUtils.getApplicationContext(); ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); for (ActivityManager.AppTask task : activityManager.getAppTasks()) { RecentTaskInfo info = DocumentUtils.getTaskInfoFromTask(task); if (info == null) continue; biggestId = Math.max(biggestId, info.persistentId); } return biggestId; } @Override public Tab openNewTab(LoadUrlParams loadUrlParams, TabLaunchType type, Tab parent, boolean incognito) { TabDelegate delegate = getTabCreator(incognito); delegate.createNewTab(loadUrlParams, type, parent); return null; } @Override public void selectModel(boolean incognito) { super.selectModel(incognito); Context context = ContextUtils.getApplicationContext(); SharedPreferences prefs = context.getSharedPreferences(PREF_PACKAGE, Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(PREF_IS_INCOGNITO_SELECTED, incognito); editor.apply(); } @Override public DocumentTabModel getModel(boolean incognito) { return (DocumentTabModel) super.getModel(incognito); } @Override public DocumentTabModel getModelForTabId(int id) { return (DocumentTabModel) super.getModelForTabId(id); } /** * Overrides the regular StorageDelegate in the constructor. MUST be called before the * DocumentTabModelSelector instance is created to take effect. */ @VisibleForTesting public static void setStorageDelegateForTests(StorageDelegate delegate) { synchronized (STORAGE_DELEGATE_FOR_TESTS_LOCK) { sStorageDelegateForTests = delegate; } } }