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

Unified Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeTabbedActivity.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f37221e02966ce4daad58f90eda26ad3119ccc9
--- /dev/null
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -0,0 +1,1314 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser;
+
+import android.app.ActivityManager;
+import android.app.SearchManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.Toast;
+
+import com.google.android.apps.chrome.R;
+
+import org.chromium.base.CommandLine;
+import org.chromium.base.MemoryPressureListener;
+import org.chromium.base.TraceEvent;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.ContextualMenuBar.ActionBarDelegate;
+import org.chromium.chrome.browser.IntentHandler.IntentHandlerDelegate;
+import org.chromium.chrome.browser.IntentHandler.TabOpenType;
+import org.chromium.chrome.browser.appmenu.AppMenuHandler;
+import org.chromium.chrome.browser.appmenu.AppMenuObserver;
+import org.chromium.chrome.browser.appmenu.ChromeAppMenuPropertiesDelegate;
+import org.chromium.chrome.browser.compositor.CompositorViewHolder;
+import org.chromium.chrome.browser.compositor.layouts.Layout;
+import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome;
+import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome.OverviewLayoutFactoryDelegate;
+import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChromePhone;
+import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChromeTablet;
+import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
+import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
+import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior.OverviewModeObserver;
+import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter;
+import org.chromium.chrome.browser.compositor.layouts.phone.StackLayout;
+import org.chromium.chrome.browser.cookies.CookiesFetcher;
+import org.chromium.chrome.browser.device.DeviceClassManager;
+import org.chromium.chrome.browser.document.DocumentUma;
+import org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkUtils;
+import org.chromium.chrome.browser.firstrun.FirstRunActivity;
+import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer;
+import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor;
+import org.chromium.chrome.browser.firstrun.FirstRunStatus;
+import org.chromium.chrome.browser.metrics.LaunchMetrics;
+import org.chromium.chrome.browser.metrics.UmaUtils;
+import org.chromium.chrome.browser.ntp.NativePageAssassin;
+import org.chromium.chrome.browser.omaha.OmahaClient;
+import org.chromium.chrome.browser.omnibox.AutocompleteController;
+import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
+import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+import org.chromium.chrome.browser.preferences.ConnectionChangeReceiver;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.preferences.bandwidth.BandwidthReductionPreferences;
+import org.chromium.chrome.browser.preferences.bandwidth.DataReductionPromoScreen;
+import org.chromium.chrome.browser.signin.SigninPromoScreen;
+import org.chromium.chrome.browser.snackbar.undo.UndoBarPopupController;
+import org.chromium.chrome.browser.sync.SyncController;
+import org.chromium.chrome.browser.tab.ChromeTab;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelUtils;
+import org.chromium.chrome.browser.tabmodel.TabWindowManager;
+import org.chromium.chrome.browser.toolbar.ToolbarControlContainer;
+import org.chromium.chrome.browser.toolbar.ToolbarHelper;
+import org.chromium.chrome.browser.util.FeatureUtilities;
+import org.chromium.chrome.browser.widget.emptybackground.EmptyBackgroundViewWrapper;
+import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager;
+import org.chromium.content.browser.ContentVideoView;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content.browser.crypto.CipherFactory;
+import org.chromium.content.common.ContentSwitches;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.ui.base.DeviceFormFactor;
+import org.chromium.ui.base.PageTransition;
+
+/**
+ * This is the main activity for ChromeMobile when not running in document mode. All the tabs
+ * are accessible via a chrome specific tab switching UI.
+ */
+public class ChromeTabbedActivity extends CompositorChromeActivity implements ActionBarDelegate,
+ OverviewModeObserver {
+
+ private static final int FIRST_RUN_EXPERIENCE_RESULT = 101;
+
+ private static final String TAG = "ChromeTabbedActivity";
+
+ private static final String HELP_URL_PREFIX = "https://support.google.com/chrome/";
+
+ private static final String FRE_RUNNING = "First run is running";
+
+ private static final String WINDOW_INDEX = "window_index";
+
+ // How long to delay closing the current tab when our app is minimized. Have to delay this
+ // so that we don't show the contents of the next tab while minimizing.
+ private static final long CLOSE_TAB_ON_MINIMIZE_DELAY_MS = 500;
+
+ // Maximum delay for initial tab creation. This is for homepage and NTP, not previous tabs
+ // restore. This is needed because we do not know when reading PartnerBrowserCustomizations
+ // provider will be finished.
+ private static final int INITIAL_TAB_CREATION_TIMEOUT_MS = 500;
+
+ /**
+ * Sending an intent with this extra sets the app into single process mode.
+ * This is only used for testing, when certain tests want to force this behaviour.
+ */
+ public static final String INTENT_EXTRA_TEST_RENDER_PROCESS_LIMIT = "render_process_limit";
+
+ /**
+ * Sending an intent with this extra disable uploading of minidumps.
+ * This is only used for testing, when certain tests want to force this behaviour.
+ */
+ public static final String INTENT_EXTRA_DISABLE_CRASH_DUMP_UPLOADING =
+ "disable_crash_dump_uploading";
+
+ /**
+ * Sending an intent with this action to Chrome will cause it to close all tabs
+ * (iff the --enable-test-intents command line flag is set). If a URL is supplied in the
+ * intent data, this will be loaded and unaffected by the close all action.
+ */
+ private static final String ACTION_CLOSE_TABS =
+ "com.google.android.apps.chrome.ACTION_CLOSE_TABS";
+
+ private ToolbarHelper mToolbarHelper;
+
+ private FindToolbarManager mFindToolbarManager;
+
+ private UndoBarPopupController mUndoBarPopupController;
+
+ private LayoutManagerChrome mLayoutManager;
+
+ private ChromeAppMenuPropertiesDelegate mChromeAppMenuPropertiesDelegate;
+ private AppMenuHandler mAppMenuHandler;
+
+ private View mMenuAnchor;
+
+ private ViewGroup mContentContainer;
+
+ private ToolbarControlContainer mControlContainer;
+
+ private TabModelSelectorImpl mTabModelSelectorImpl;
+ private TabModelSelectorTabObserver mTabModelSelectorTabObserver;
+ private TabModelObserver mTabModelObserver;
+
+ private ConnectionChangeReceiver mConnectionChangeReceiver;
+
+ private boolean mUIInitialized = false;
+
+ private boolean mIsOnFirstRun = false;
+
+ /**
+ * Keeps track of whether or not a specific tab was created based on the startup intent.
+ */
+ private boolean mCreatedTabOnStartup = false;
+
+ // Whether or not chrome was launched with an intent to open a tab.
+ private boolean mIntentWithEffect = false;
+
+ // Time at which an intent was received and handled.
+ private long mIntentHandlingTimeMs = 0;
+
+ @Override
+ public void initializeCompositor() {
+ try {
+ TraceEvent.begin("ChromeTabbedActivity.initializeCompositor");
+ super.initializeCompositor();
+
+ mTabModelSelectorImpl.onNativeLibraryReady(getTabContentManager());
+
+ mTabModelObserver = new EmptyTabModelObserver() {
+ @Override
+ public void didCloseTab(Tab tab) {
+ closeIfNoTabsAndHomepageEnabled();
+ }
+
+ @Override
+ public void tabPendingClosure(Tab tab) {
+ closeIfNoTabsAndHomepageEnabled();
+ }
+
+ private void closeIfNoTabsAndHomepageEnabled() {
+ // If the last tab is closed, and homepage is enabled, then exit Chrome.
+ if (HomepageManager.isHomepageEnabled(getApplicationContext())
+ && getTabModelSelector().getTotalTabCount() == 0) {
+ finish();
+ }
+ }
+
+ @Override
+ public void didAddTab(Tab tab, TabLaunchType type) {
+ if (type == TabLaunchType.FROM_LONGPRESS_BACKGROUND
+ && !DeviceClassManager.enableAnimations(getApplicationContext())) {
+ Toast.makeText(getBaseContext(),
+ R.string.open_in_new_tab_toast,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ };
+ for (TabModel model : mTabModelSelectorImpl.getModels()) {
+ model.addObserver(mTabModelObserver);
+ }
+
+ Bundle state = getSavedInstanceState();
+ if (state != null && state.containsKey(FRE_RUNNING)) {
+ mIsOnFirstRun = state.getBoolean(FRE_RUNNING);
+ }
+ } finally {
+ TraceEvent.end("ChromeTabbedActivity.initializeCompositor");
+ }
+ }
+
+ private void refreshSignIn() {
+ if (mIsOnFirstRun) return;
+ android.util.Log.i(TAG, "in refreshSignIn before starting the sign-in processor");
+ FirstRunSignInProcessor.start(this);
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ mIntentHandlingTimeMs = SystemClock.uptimeMillis();
+ super.onNewIntent(intent);
+ }
+
+ @Override
+ public void finishNativeInitialization() {
+ try {
+ TraceEvent.begin("ChromeTabbedActivity.finishNativeInitialization");
+
+ launchFirstRunExperience();
+
+ ChromePreferenceManager preferenceManager = ChromePreferenceManager.getInstance(this);
+ // Promos can only be shown when we start with ACTION_MAIN intent and
+ // after FRE is complete.
+ if (!mIntentWithEffect && FirstRunStatus.getFirstRunFlowComplete(this)) {
+ // Only show promos on the second oppurtunity. This is because we show FRE on the
+ // first oppurtunity, and we don't want to show such content back to back.
+ if (preferenceManager.getPromosSkippedOnFirstStart()) {
+ // Data reduction promo should be temporarily suppressed if the sign in promo is
+ // shown to avoid nagging users too much.
+ if (!SigninPromoScreen.launchSigninPromoIfNeeded(this)) {
+ DataReductionPromoScreen.launchDataReductionPromo(this);
+ }
+ } else {
+ preferenceManager.setPromosSkippedOnFirstStart(true);
+ }
+ }
+
+ refreshSignIn();
+
+ initializeUI();
+
+ // The dataset has already been created, we need to initialize our state.
+ mTabModelSelectorImpl.notifyChanged();
+
+ getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
+ Window.PROGRESS_VISIBILITY_OFF);
+
+ super.finishNativeInitialization();
+
+ if (getActivityTab() != null) {
+ BandwidthReductionPreferences.launchDataReductionSSLInfoBar(
+ this, getActivityTab().getWebContents());
+ }
+ } finally {
+ TraceEvent.end("ChromeTabbedActivity.finishNativeInitialization");
+ }
+ }
+
+ @Override
+ public void onResumeWithNative() {
+ super.onResumeWithNative();
+ CookiesFetcher.restoreCookies(this);
+ }
+
+ @Override
+ public void onPauseWithNative() {
+ mTabModelSelectorImpl.commitAllTabClosures();
+ CookiesFetcher.persistCookies(this);
+ super.onPauseWithNative();
+ }
+
+ @Override
+ public void onStopWithNative() {
+ super.onStopWithNative();
+ mTabModelSelectorImpl.saveState();
+ try {
+ getConnectionChangeReceiver().unregisterReceiver(ChromeTabbedActivity.this);
+ } catch (IllegalArgumentException e) {
+ // This may happen when onStop get called very early in UI test.
+ }
+
+ // Dismiss the popup menu if it is showing.
+ hideMenus();
+ }
+
+ @Override
+ public void onStartWithNative() {
+ super.onStartWithNative();
+ // If we don't have a current tab, show the overview mode.
+ if (getActivityTab() == null) mLayoutManager.showOverview(false);
+
+ getConnectionChangeReceiver().registerReceiver(ChromeTabbedActivity.this);
+
+ resetSavedInstanceState();
+
+ if (FeatureUtilities.isDocumentModeEligible(this)) {
+ DocumentUma.recordInDocumentMode(false);
+ }
+ }
+
+ @Override
+ public void onNewIntentWithNative(Intent intent) {
+ try {
+ TraceEvent.begin("ChromeTabbedActivity.onNewIntentWithNative");
+
+ super.onNewIntentWithNative(intent);
+ if (CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_TEST_INTENTS)) {
+ handleDebugIntent(intent);
+ }
+ } finally {
+ TraceEvent.end("ChromeTabbedActivity.onNewIntentWithNative");
+ }
+ }
+
+ private void handleDebugIntent(Intent intent) {
+ if (ACTION_CLOSE_TABS.equals(intent.getAction())) {
+ getTabModelSelector().closeAllTabs();
+ } else if (MemoryPressureListener.handleDebugIntent(ChromeTabbedActivity.this,
+ intent.getAction())) {
+ // Handled.
+ }
+ }
+
+ private static class StackLayoutFactory implements OverviewLayoutFactoryDelegate {
+ @Override
+ public Layout createOverviewLayout(Context context, LayoutUpdateHost updateHost,
+ LayoutRenderHost renderHost, EventFilter eventFilter) {
+ return new StackLayout(context, updateHost, renderHost, eventFilter);
+ }
+ }
+
+ private void initializeUI() {
+ try {
+ TraceEvent.begin("ChromeTabbedActivity.initializeUI");
+
+ CommandLine commandLine = CommandLine.getInstance();
+
+ commandLine.appendSwitch(ContentSwitches.ENABLE_INSTANT_EXTENDED_API);
+
+ CompositorViewHolder compositorViewHolder = getCompositorViewHolder();
+ if (DeviceFormFactor.isTablet(this)) {
+ boolean enableTabSwitcher =
+ CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABLE_TABLET_TAB_STACK);
+ mLayoutManager = new LayoutManagerChromeTablet(compositorViewHolder,
+ enableTabSwitcher ? new StackLayoutFactory() : null);
+ } else {
+ mLayoutManager = new LayoutManagerChromePhone(compositorViewHolder,
+ new StackLayoutFactory());
+ }
+
+ mLayoutManager.addOverviewModeObserver(this);
+
+ // TODO(yusufo): get rid of findViewById(R.id.url_bar).
+ initializeCompositorContent(mLayoutManager, findViewById(R.id.url_bar),
+ mContentContainer, mControlContainer);
+
+ mTabModelSelectorImpl.setOverviewModeBehavior(mLayoutManager);
+
+ mUndoBarPopupController.initialize();
+
+ // Adjust the content container if we're not entering fullscreen mode.
+ if (getFullscreenManager() == null) {
+ float controlHeight = getResources().getDimension(R.dimen.control_container_height);
+ ((FrameLayout.LayoutParams) mContentContainer.getLayoutParams()).topMargin =
+ (int) controlHeight;
+ }
+
+ // Bootstrap the first tab as it may have been created before initializing the
+ // fullscreen manager.
+ if (mTabModelSelectorImpl != null && mTabModelSelectorImpl.getCurrentTab() != null) {
+ mTabModelSelectorImpl.getCurrentTab().setFullscreenManager(getFullscreenManager());
+ }
+
+ mAppMenuHandler.addObserver(new AppMenuObserver() {
+ @Override
+ public void onMenuVisibilityChanged(boolean isVisible) {
+ if (!isVisible) {
+ mChromeAppMenuPropertiesDelegate.onMenuDismissed();
+ }
+ }
+ });
+
+ mFindToolbarManager = new FindToolbarManager(this, getTabModelSelector(),
+ mToolbarHelper.getContextualMenuBar().getCustomSelectionActionModeCallback());
+ mControlContainer.setFindToolbarManager(mFindToolbarManager);
+ if (getContextualSearchManager() != null) {
+ getContextualSearchManager().setFindToolbarManager(mFindToolbarManager);
+ }
+
+ OnClickListener tabSwitcherClickHandler = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ toggleOverview();
+ }
+ };
+ OnClickListener newTabClickHandler = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // This assumes that the keyboard can not be seen at the same time as the
+ // newtab button on the toolbar.
+ getCurrentTabCreator().launchNTP();
+ }
+ };
+ OnClickListener bookmarkClickHandler = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ addOrEditBookmark(getActivityTab());
+ }
+ };
+
+ mToolbarHelper.initializeControls(mFindToolbarManager, mLayoutManager, mLayoutManager,
+ tabSwitcherClickHandler, newTabClickHandler, bookmarkClickHandler, null);
+
+ mMenuAnchor = findViewById(R.id.menu_anchor_stub);
+
+ removeWindowBackground();
+
+ if (mIsTablet) {
+ EmptyBackgroundViewWrapper bgViewWrapper = new EmptyBackgroundViewWrapper(
+ getTabModelSelector(), getTabCreator(false), ChromeTabbedActivity.this,
+ mAppMenuHandler, mLayoutManager);
+ bgViewWrapper.initialize();
+ }
+
+ mLayoutManager.hideOverview(false);
+
+ mUIInitialized = true;
+ } finally {
+ TraceEvent.end("ChromeTabbedActivity.initializeUI");
+ }
+ }
+
+ @Override
+ public void initializeState() {
+ // This method goes through 3 steps:
+ // 1. Load the saved tab state (but don't start restoring the tabs yet).
+ // 2. Process the Intent that this activity received and if that should result in any
+ // new tabs, create them. This is done after step 1 so that the new tab gets
+ // created after previous tab state was restored.
+ // 3. If no tabs were created in any of the above steps, create an NTP, otherwise
+ // start asynchronous tab restore (loading the previously active tab synchronously
+ // if no new tabs created in step 2).
+
+ // Only look at the original intent if this is not a "restoration" and we are allowed to
+ // process intents. Any subsequent intents are carried through onNewIntent.
+ try {
+ TraceEvent.begin("ChromeTabbedActivity.initializeState");
+
+ super.initializeState();
+
+ Intent intent = getIntent();
+
+ if (!CipherFactory.getInstance().restoreFromBundle(getSavedInstanceState())) {
+ mTabModelSelectorImpl.clearEncryptedState();
+ }
+
+ boolean noRestoreState =
+ CommandLine.getInstance().hasSwitch(ChromeSwitches.NO_RESTORE_STATE);
+ if (noRestoreState) {
+ // Clear the state files because they are inconsistent and useless from now on.
+ mTabModelSelectorImpl.clearState();
+ } else if (!mIsOnFirstRun) {
+ // State should be clear when we start first run and hence we do not need to load
+ // a previous state. This may change the current Model, watch out for initialization
+ // based on the model.
+ mTabModelSelectorImpl.loadState();
+ }
+
+ mIntentWithEffect = false;
+ if ((mIsOnFirstRun || getSavedInstanceState() == null) && intent != null
+ && !mIntentHandler.shouldIgnoreIntent(ChromeTabbedActivity.this, intent)) {
+ mIntentWithEffect = mIntentHandler.onNewIntent(intent);
+ }
+
+ mCreatedTabOnStartup = getCurrentTabModel().getCount() > 0
+ || mTabModelSelectorImpl.getRestoredTabCount() > 0
+ || mIntentWithEffect;
+
+ // We always need to try to restore tabs. The set of tabs might be empty, but at least
+ // it will trigger the notification that tab restore is complete which is needed by
+ // other parts of Chrome such as sync.
+ boolean activeTabBeingRestored = !mIntentWithEffect;
+ mTabModelSelectorImpl.restoreTabs(activeTabBeingRestored);
+
+ // Only create an initial tab if no tabs were restored and no intent was handled.
+ // Also, check whether the active tab was supposed to be restored and that the total
+ // tab count is now non zero. If this is not the case, tab restore failed and we need
+ // to create a new tab as well.
+ if (!mCreatedTabOnStartup
+ || (activeTabBeingRestored && getTabModelSelector().getTotalTabCount() == 0)) {
+ // If homepage URI is not determined, due to PartnerBrowserCustomizations provider
+ // async reading, then create a tab at the async reading finished. If it takes
+ // too long, just create NTP.
+ PartnerBrowserCustomizations.setOnInitializeAsyncFinished(
+ new Runnable() {
+ @Override
+ public void run() {
+ createInitialTab();
+ }
+ }, INITIAL_TAB_CREATION_TIMEOUT_MS);
+ }
+
+ RecordHistogram.recordBooleanHistogram(
+ "MobileStartup.ColdStartupIntent", mIntentWithEffect);
+ } finally {
+ TraceEvent.end("ChromeTabbedActivity.initializeState");
+ }
+ }
+
+ /**
+ * Create an initial tab for cold start without restored tabs.
+ */
+ private void createInitialTab() {
+ String url = HomepageManager.getHomepageUri(getApplicationContext());
+ if (TextUtils.isEmpty(url)) url = UrlConstants.NTP_URL;
+ getTabCreator(false).createNewTab(
+ new LoadUrlParams(url), TabLaunchType.FROM_MENU_OR_OVERVIEW, null);
+ }
+
+ @Override
+ public boolean onActivityResultWithNative(int requestCode, int resultCode, Intent data) {
+ if (super.onActivityResultWithNative(requestCode, resultCode, data)) return true;
+
+ if (requestCode == FIRST_RUN_EXPERIENCE_RESULT) {
+ mIsOnFirstRun = false;
+ if (resultCode == RESULT_OK) {
+ refreshSignIn();
+ } else {
+ if (data != null && data.getBooleanExtra(
+ FirstRunActivity.RESULT_CLOSE_APP, false)) {
+ getTabModelSelector().closeAllTabs(true);
+ finish();
+ } else {
+ launchFirstRunExperience();
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onOrientationChange(int orientation) {
+ super.onOrientationChange(orientation);
+ mToolbarHelper.onOrientationChange();
+ }
+
+ @Override
+ public void onAccessibilityModeChanged(boolean enabled) {
+ super.onAccessibilityModeChanged(enabled);
+ if (mToolbarHelper != null) mToolbarHelper.onAccessibilityStatusChanged(enabled);
+
+ if (mLayoutManager != null) {
+ mLayoutManager.setEnableAnimations(
+ DeviceClassManager.enableAnimations(getApplicationContext()));
+ }
+ if (mIsTablet) {
+ if (getCompositorViewHolder() != null) {
+ getCompositorViewHolder().onAccessibilityStatusChanged(enabled);
+ }
+ }
+ if (mLayoutManager != null && mLayoutManager.overviewVisible()) {
+ mLayoutManager.hideOverview(false);
+ }
+ }
+
+ /**
+ * Internal class which performs the intent handling operations delegated by IntentHandler.
+ */
+ private class InternalIntentDelegate implements IntentHandler.IntentHandlerDelegate {
+
+ /**
+ * Processes a url view intent.
+ *
+ * @param url The url from the intent.
+ */
+ @Override
+ public void processUrlViewIntent(String url, String headers, TabOpenType tabOpenType,
+ String externalAppId, int tabIdToBringToFront, Intent intent) {
+ TabModel tabModel = getCurrentTabModel();
+ switch (tabOpenType) {
+ case REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB:
+ // Used by the bookmarks application.
+ if (tabModel.getCount() > 0 && mUIInitialized
+ && mLayoutManager.overviewVisible()) {
+ mLayoutManager.hideOverview(true);
+ }
+ mTabModelSelectorImpl.tryToRestoreTabStateForUrl(url);
+ int tabToBeClobberedIndex = TabModelUtils.getTabIndexByUrl(tabModel, url);
+ Tab tabToBeClobbered = tabModel.getTabAt(tabToBeClobberedIndex);
+ if (tabToBeClobbered != null) {
+ TabModelUtils.setIndex(tabModel, tabToBeClobberedIndex);
+ tabToBeClobbered.reload();
+ RecordUserAction.record("MobileTabClobbered");
+ } else {
+ launchIntent(url, headers, externalAppId, true, intent);
+ }
+ RecordUserAction.record("MobileReceivedExternalIntent");
+ LaunchMetrics.recordHomeScreenLaunchIntoTab(url);
+ break;
+ case REUSE_APP_ID_MATCHING_TAB_ELSE_NEW_TAB:
+ launchIntent(url, headers, externalAppId, false, intent);
+ RecordUserAction.record("MobileReceivedExternalIntent");
+ break;
+ case BRING_TAB_TO_FRONT:
+ mTabModelSelectorImpl.tryToRestoreTabStateForId(tabIdToBringToFront);
+
+ int tabIndex = TabModelUtils.getTabIndexById(tabModel, tabIdToBringToFront);
+ if (tabIndex == TabModel.INVALID_TAB_INDEX) {
+ TabModel otherModel =
+ getTabModelSelector().getModel(!tabModel.isIncognito());
+ tabIndex = TabModelUtils.getTabIndexById(otherModel, tabIdToBringToFront);
+ if (tabIndex != TabModel.INVALID_TAB_INDEX) {
+ getTabModelSelector().selectModel(otherModel.isIncognito());
+ TabModelUtils.setIndex(otherModel, tabIndex);
+ }
+ } else {
+ TabModelUtils.setIndex(tabModel, tabIndex);
+ }
+ RecordUserAction.record("MobileReceivedExternalIntent");
+ break;
+ case CLOBBER_CURRENT_TAB:
+ // The browser triggered the intent. This happens when clicking links which
+ // can be handled by other applications (e.g. www.youtube.com links).
+ ChromeTab currentTab = ChromeTab.fromTab(getActivityTab());
+ if (currentTab != null) {
+ currentTab.getTabRedirectHandler().updateIntent(intent);
+ int transitionType = PageTransition.LINK | PageTransition.FROM_API;
+ LoadUrlParams loadUrlParams = new LoadUrlParams(url, transitionType);
+ loadUrlParams.setIntentReceivedTimestamp(mIntentHandlingTimeMs);
+ currentTab.loadUrl(loadUrlParams);
+ RecordUserAction.record("MobileTabClobbered");
+ } else {
+ launchIntent(url, headers, externalAppId, true, intent);
+ }
+ break;
+ case OPEN_NEW_TAB:
+ launchIntent(url, headers, externalAppId, true, intent);
+ RecordUserAction.record("MobileReceivedExternalIntent");
+ break;
+ case OPEN_NEW_INCOGNITO_TAB:
+ if (url == null || url.equals(UrlConstants.NTP_URL)) {
+ if (TextUtils.equals(externalAppId, getPackageName())) {
+ // Used by the Account management screen to open a new incognito tab.
+ // Account management screen collects its metrics separately.
+ getTabCreator(true).launchUrl(
+ UrlConstants.NTP_URL, TabLaunchType.FROM_MENU_OR_OVERVIEW);
+ } else {
+ getTabCreator(true).launchUrl(
+ UrlConstants.NTP_URL, TabLaunchType.FROM_EXTERNAL_APP);
+ RecordUserAction.record("MobileReceivedExternalIntent");
+ }
+ } else {
+ if (TextUtils.equals(externalAppId, getPackageName())) {
+ getTabCreator(true).launchUrl(
+ url, TabLaunchType.FROM_LINK, intent, mIntentHandlingTimeMs);
+ } else {
+ getTabCreator(true).launchUrlFromExternalApp(url, headers,
+ externalAppId, true, intent, mIntentHandlingTimeMs);
+ RecordUserAction.record("MobileReceivedExternalIntent");
+ }
+ }
+ break;
+ default:
+ assert false : "Unknown TabOpenType: " + tabOpenType;
+ break;
+ }
+ mToolbarHelper.setUrlBarFocus(false);
+ }
+
+ @Override
+ public void processWebSearchIntent(String query) {
+ Intent searchIntent = new Intent(Intent.ACTION_WEB_SEARCH);
+ searchIntent.putExtra(SearchManager.QUERY, query);
+ startActivity(searchIntent);
+ }
+ }
+
+ @Override
+ public boolean isStartedUpCorrectly(Intent intent) {
+ if (FeatureUtilities.isDocumentMode(this)) {
+ Log.e(TAG, "Discarding Intent: Starting ChromeTabbedActivity in Document mode");
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void preInflationStartup() {
+ super.preInflationStartup();
+
+ // Decide whether to record startup UMA histograms. This is done early in the main
+ // Activity.onCreate() to avoid recording navigation delays when they require user input to
+ // proceed. For example, FRE (First Run Experience) happens before the activity is created,
+ // and triggers initialization of the native library. At the moment it seems safe to assume
+ // that uninitialized native library is an indication of an application start that is
+ // followed by navigation immediately without user input.
+ if (!LibraryLoader.isInitialized()) {
+ UmaUtils.setRunningApplicationStart(true);
+ }
+
+ CommandLine commandLine = CommandLine.getInstance();
+ if (commandLine.hasSwitch(ContentSwitches.ENABLE_TEST_INTENTS)
+ && getIntent() != null
+ && getIntent().hasExtra(
+ ChromeTabbedActivity.INTENT_EXTRA_TEST_RENDER_PROCESS_LIMIT)) {
+ int value = getIntent().getIntExtra(
+ ChromeTabbedActivity.INTENT_EXTRA_TEST_RENDER_PROCESS_LIMIT, -1);
+ if (value != -1) {
+ String[] args = new String[1];
+ args[0] = "--" + ChromeSwitches.RENDER_PROCESS_LIMIT
+ + "=" + Integer.toString(value);
+ commandLine.appendSwitchesAndArguments(args);
+ }
+ }
+
+ commandLine.appendSwitch(ChromeSwitches.ENABLE_HIGH_END_UI_UNDO);
+
+ supportRequestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY);
+
+ // We are starting from history with a URL after data has been cleared. On Samsung this
+ // can happen after user clears data and clicks on a recents item on pre-L devices.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
+ && getIntent().getData() != null
+ && (getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0
+ && OmahaClient.isFreshInstallOrDataHasBeenCleared(getApplicationContext())) {
+ getIntent().setData(null);
+ }
+ }
+
+ @Override
+ protected int getControlContainerLayoutId() {
+ return R.layout.control_container;
+ }
+
+ @Override
+ public void postInflationStartup() {
+ super.postInflationStartup();
+
+ // Critical path for startup. Create the minimum objects needed
+ // to allow a blank screen draw (without depending on any native code)
+ // and then yield ASAP.
+ createTabModelSelectorImpl(getSavedInstanceState());
+
+ if (isFinishing()) return;
+
+ // Don't show the keyboard until user clicks in.
+ getWindow().setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
+ | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
+
+ mContentContainer = (ViewGroup) findViewById(android.R.id.content);
+ mControlContainer = (ToolbarControlContainer) findViewById(R.id.control_container);
+
+ mUndoBarPopupController = new UndoBarPopupController(this, mTabModelSelectorImpl,
+ getSnackbarManager());
+
+ mChromeAppMenuPropertiesDelegate = new ChromeAppMenuPropertiesDelegate(this);
+ mAppMenuHandler = new AppMenuHandler(ChromeTabbedActivity.this,
+ mChromeAppMenuPropertiesDelegate, R.menu.main_menu);
+ mToolbarHelper = new ToolbarHelper(this, mControlContainer, mAppMenuHandler,
+ mChromeAppMenuPropertiesDelegate, getCompositorViewHolder().getInvalidator());
+ }
+
+ /**
+ * Launch the First Run flow to set up Chrome.
+ * There are two different pathways that can occur:
+ * 1) The First Run Experience activity is run, which walks the user through the ToS, signing
+ * in, and turning on UMA reporting. This happens in most cases.
+ * 2) We automatically try to sign-in the user and skip the FRE activity, then ask the user to
+ * turn on UMA reporting some time later using an InfoBar. This happens if Chrome is opened
+ * with an Intent to view a URL, or if we're on a Nexus device where the user has already
+ * been exposed to the ToS and Privacy Notice.
+ */
+ private void launchFirstRunExperience() {
+ SyncController.get(this).setDelaySync(false);
+ if (mIsOnFirstRun) {
+ mTabModelSelectorImpl.clearState();
+ return;
+ }
+
+ final boolean isIntentActionMain = getIntent() != null
+ && TextUtils.equals(getIntent().getAction(), Intent.ACTION_MAIN);
+ android.util.Log.i(TAG, "begin FirstRunFlowSequencer.checkIfFirstRunIsNecessary");
+ final Intent freIntent = FirstRunFlowSequencer.checkIfFirstRunIsNecessary(
+ this, getIntent(), isIntentActionMain);
+ android.util.Log.i(TAG, "end FirstRunFlowSequencer.checkIfFirstRunIsNecessary");
+ if (freIntent == null) return;
+
+ mIsOnFirstRun = true;
+
+ // TODO(dtrainor): Investigate this further and revert once Android pushes fix?
+ // Posting this due to Android bug where we apparently are stopping a
+ // non-resumed activity. That statement looks incorrect, but need to not hit
+ // the runtime exception here.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ startActivityForResult(freIntent, FIRST_RUN_EXPERIENCE_RESULT);
+ }
+ });
+ }
+
+ @Override
+ protected void onDeferredStartup() {
+ try {
+ TraceEvent.begin("ChromeTabbedActivity.onDeferredStartup");
+ super.onDeferredStartup();
+
+ mToolbarHelper.onDeferredStartup();
+
+ ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ RecordHistogram.recordSparseSlowlyHistogram(
+ "MemoryAndroid.DeviceMemoryClass", am.getMemoryClass());
+
+ AutocompleteController.nativePrefetchZeroSuggestResults();
+ } finally {
+ TraceEvent.end("ChromeTabbedActivity.onDeferredStartup");
+ }
+ }
+
+ private void createTabModelSelectorImpl(Bundle savedInstanceState) {
+ // We determine the model as soon as possible so every systems get initialized coherently.
+ boolean startIncognito = savedInstanceState != null
+ && savedInstanceState.getBoolean("is_incognito_selected", false);
+ int index = savedInstanceState != null ? savedInstanceState.getInt(WINDOW_INDEX, 0) : 0;
+ mTabModelSelectorImpl = (TabModelSelectorImpl)
+ TabWindowManager.getInstance().requestSelector(this, getWindowAndroid(), index);
+ if (mTabModelSelectorImpl == null) {
+ Toast.makeText(this, getString(R.string.unsupported_number_of_windows),
+ Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+ mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(mTabModelSelectorImpl) {
+
+ private boolean mIsFirstPageLoadStart = true;
+
+ @Override
+ public void onPageLoadStarted(Tab tab) {
+ // Discard startup navigation measurements when the user interfered and started the
+ // 2nd navigation (in activity lifetime) in parallel.
+ if (!mIsFirstPageLoadStart) {
+ UmaUtils.setRunningApplicationStart(false);
+ } else {
+ mIsFirstPageLoadStart = false;
+ }
+ }
+ };
+
+ if (startIncognito) mTabModelSelectorImpl.selectModel(true);
+ setTabModelSelector(mTabModelSelectorImpl);
+ }
+
+ @Override
+ public void terminateIncognitoSession() {
+ getTabModelSelector().getModel(true).closeAllTabs();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ hideMenus();
+ super.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ public boolean onMenuOrKeyboardAction(final int id, boolean fromMenu) {
+ final Tab currentTab = getActivityTab();
+ if (id == R.id.new_tab_menu_id) {
+ Tab launchedTab = getTabCreator(false).launchUrl(
+ UrlConstants.NTP_URL,
+ fromMenu ? TabLaunchType.FROM_MENU_OR_OVERVIEW : TabLaunchType.FROM_KEYBOARD);
+ RecordUserAction.record("MobileMenuNewTab");
+ RecordUserAction.record("MobileNewTabOpened");
+ if (mIsTablet && !fromMenu && !launchedTab.isHidden()) {
+ mToolbarHelper.setUrlBarFocus(true);
+ }
+ } else if (id == R.id.new_incognito_tab_menu_id) {
+ if (PrefServiceBridge.getInstance().isIncognitoModeEnabled()) {
+ // This action must be recorded before opening the incognito tab since UMA actions
+ // are dropped when an incognito tab is open.
+ RecordUserAction.record("MobileMenuNewIncognitoTab");
+ RecordUserAction.record("MobileNewTabOpened");
+ Tab launchedTab = getTabCreator(true).launchUrl(
+ UrlConstants.NTP_URL,
+ fromMenu ? TabLaunchType.FROM_MENU_OR_OVERVIEW
+ : TabLaunchType.FROM_KEYBOARD);
+ if (mIsTablet && !fromMenu && !launchedTab.isHidden()) {
+ mToolbarHelper.setUrlBarFocus(true);
+ }
+ }
+ } else if (id == R.id.all_bookmarks_menu_id) {
+ if (currentTab != null) {
+ getCompositorViewHolder().hideKeyboard(new Runnable() {
+ @Override
+ public void run() {
+ if (!EnhancedBookmarkUtils.showEnhancedBookmarkIfEnabled(
+ ChromeTabbedActivity.this)) {
+ currentTab.loadUrl(new LoadUrlParams(
+ UrlConstants.BOOKMARKS_URL,
+ PageTransition.AUTO_BOOKMARK));
+ }
+ }
+ });
+ RecordUserAction.record("MobileMenuAllBookmarks");
+ }
+ } else if (id == R.id.recent_tabs_menu_id) {
+ if (currentTab != null) {
+ currentTab.loadUrl(new LoadUrlParams(
+ UrlConstants.RECENT_TABS_URL,
+ PageTransition.AUTO_BOOKMARK));
+ RecordUserAction.record("MobileMenuOpenTabs");
+ }
+ } else if (id == R.id.close_all_tabs_menu_id) {
+ // Close both incognito and normal tabs
+ getTabModelSelector().closeAllTabs();
+ RecordUserAction.record("MobileMenuCloseAllTabs");
+ } else if (id == R.id.close_all_incognito_tabs_menu_id) {
+ // Close only incognito tabs
+ getTabModelSelector().getModel(true).closeAllTabs();
+ // TODO(nileshagrawal) Record unique action for this. See bug http://b/5542946.
+ RecordUserAction.record("MobileMenuCloseAllTabs");
+ } else if (id == R.id.find_in_page_id) {
+ mFindToolbarManager.showToolbar();
+ if (fromMenu) {
+ RecordUserAction.record("MobileMenuFindInPage");
+ } else {
+ RecordUserAction.record("MobileShortcutFindInPage");
+ }
+ } else if (id == R.id.show_menu) {
+ showMenu();
+ } else if (id == R.id.focus_url_bar) {
+ boolean isUrlBarVisible = !mLayoutManager.overviewVisible()
+ && (!mIsTablet || getCurrentTabModel().getCount() != 0);
+ if (isUrlBarVisible) {
+ mToolbarHelper.setUrlBarFocus(true);
+ }
+ } else {
+ return super.onMenuOrKeyboardAction(id, fromMenu);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean handleBackPressed() {
+ if (!mUIInitialized) return false;
+ final Tab currentTab = getActivityTab();
+
+ if (currentTab == null) {
+ if (mToolbarHelper.back()) {
+ RecordUserAction.record("SystemBackForNavigation");
+ RecordUserAction.record("MobileTabClobbered");
+ } else {
+ moveTaskToBack(true);
+ }
+ RecordUserAction.record("SystemBack");
+ return true;
+ }
+
+ // If we are in overview mode and not a tablet, then leave overview mode on back.
+ if (mLayoutManager.overviewVisible() && !mIsTablet) {
+ mLayoutManager.hideOverview(true);
+ // TODO(benm): Record any user metrics in this case?
+ return true;
+ }
+
+ if (isFullscreenVideoPlaying()) {
+ ContentVideoView.getContentVideoView().exitFullscreen(false);
+ return true;
+ }
+
+ if (getFullscreenManager().getPersistentFullscreenMode()) {
+ getFullscreenManager().setPersistentFullscreenMode(false);
+ return true;
+ }
+
+ if (!mToolbarHelper.back()) {
+ final TabLaunchType type = currentTab.getLaunchType();
+ final String associatedApp = currentTab.getAppAssociatedWith();
+ final int parentId = currentTab.getParentId();
+ final boolean helpUrl = currentTab.getUrl().startsWith(HELP_URL_PREFIX);
+
+ // If the current tab url is HELP_URL, then the back button should close the tab to
+ // get back to the previous state. The reason for startsWith check is that the
+ // actual redirected URL is a different system language based help url.
+ if (type == TabLaunchType.FROM_MENU_OR_OVERVIEW && helpUrl) {
+ getCurrentTabModel().closeTab(currentTab);
+ return true;
+ }
+
+ // [true]: Reached the bottom of the back stack on a tab the user did not explicitly
+ // create (i.e. it was created by an external app or opening a link in background, etc).
+ // [false]: Reached the bottom of the back stack on a tab that the user explicitly
+ // created (e.g. selecting "new tab" from menu).
+ final boolean shouldCloseTab = type == TabLaunchType.FROM_LINK
+ || type == TabLaunchType.FROM_EXTERNAL_APP
+ || type == TabLaunchType.FROM_LONGPRESS_FOREGROUND
+ || type == TabLaunchType.FROM_LONGPRESS_BACKGROUND
+ || (type == TabLaunchType.FROM_RESTORE && parentId != Tab.INVALID_TAB_ID);
+
+ // Minimize the app if either:
+ // - we decided not to close the tab
+ // - we decided to close the tab, but it was opened by an external app, so we will go
+ // exit Chrome on top of closing the tab
+ final boolean minimizeApp = !shouldCloseTab || (type == TabLaunchType.FROM_EXTERNAL_APP
+ && !TextUtils.equals(associatedApp, getPackageName()));
+
+ if (minimizeApp) {
+ moveTaskToBack(true);
+ if (shouldCloseTab) {
+ // In the case of closing a tab upon minimalization, don't allow the close
+ // action to happen until after our app is minimized to make sure we don't get a
+ // brief glimpse of the newly active tab before we exit Chrome.
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ getCurrentTabModel().closeTab(currentTab, false, true, false);
+ }
+ }, CLOSE_TAB_ON_MINIMIZE_DELAY_MS);
+ }
+ } else if (shouldCloseTab) {
+ getCurrentTabModel().closeTab(currentTab, true, false, false);
+ }
+ } else {
+ RecordUserAction.record("SystemBackForNavigation");
+ RecordUserAction.record("MobileTabClobbered");
+ }
+ RecordUserAction.record("SystemBack");
+
+ return true;
+ }
+
+ /**
+ * Launch a URL from an intent.
+ *
+ * @param url The url from the intent.
+ * @param headers Optional headers to be sent when opening the URL.
+ * @param externalAppId External app id.
+ * @param forceNewTab Whether to force the URL to be launched in a new tab or to fall
+ * back to the default behavior for making that determination.
+ * @param intent The original intent.
+ */
+ private void launchIntent(String url, String headers, String externalAppId,
+ boolean forceNewTab, Intent intent) {
+ if (mUIInitialized) {
+ mLayoutManager.hideOverview(false);
+ mToolbarHelper.finishAnimations();
+ }
+ if (TextUtils.equals(externalAppId, getPackageName())) {
+ // If the intent was launched by chrome, open the new tab in the current model.
+ // Using FROM_LINK ensures the tab is parented to the current tab, which allows
+ // the back button to close these tabs and restore selection to the previous tab.
+ getCurrentTabCreator().launchUrl(url, TabLaunchType.FROM_LINK, intent,
+ mIntentHandlingTimeMs);
+ } else {
+ getTabCreator(false).launchUrlFromExternalApp(url, headers, externalAppId,
+ forceNewTab, intent, mIntentHandlingTimeMs);
+ }
+ }
+
+ private void toggleOverview() {
+ Tab currentTab = getActivityTab();
+ ContentViewCore contentViewCore =
+ currentTab != null ? currentTab.getContentViewCore() : null;
+
+ if (!mLayoutManager.overviewVisible()) {
+ getCompositorViewHolder().hideKeyboard(new Runnable() {
+ @Override
+ public void run() {
+ mLayoutManager.showOverview(true);
+ }
+ });
+ if (contentViewCore != null) {
+ contentViewCore.setAccessibilityState(false);
+ }
+ } else if (getCurrentTabModel().getCount() != 0) {
+ // Don't hide overview if current tab stack is empty()
+ mLayoutManager.hideOverview(true);
+
+ // hideOverview could change the current tab. Update the local variables.
+ currentTab = getActivityTab();
+ contentViewCore = currentTab != null ? currentTab.getContentViewCore() : null;
+
+ if (contentViewCore != null) {
+ contentViewCore.setAccessibilityState(true);
+ }
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ CipherFactory.getInstance().saveToBundle(outState);
+ outState.putBoolean("is_incognito_selected", getCurrentTabModel().isIncognito());
+ outState.putBoolean(FRE_RUNNING, mIsOnFirstRun);
+ outState.putInt(WINDOW_INDEX,
+ TabWindowManager.getInstance().getIndexForWindow(this));
+ }
+
+ @Override
+ public void onDestroyInternal() {
+ if (mLayoutManager != null) mLayoutManager.removeOverviewModeObserver(this);
+ if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy();
+ if (mTabModelObserver != null) {
+ for (TabModel model : mTabModelSelectorImpl.getModels()) {
+ model.removeObserver(mTabModelObserver);
+ }
+ }
+ if (mToolbarHelper != null) mToolbarHelper.destroy();
+ if (mUndoBarPopupController != null) mUndoBarPopupController.destroy();
+ super.onDestroyInternal();
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ // The conditions are expressed using ranges to capture intermediate levels possibly added
+ // to the API in the future.
+ if ((level >= TRIM_MEMORY_RUNNING_LOW && level < TRIM_MEMORY_UI_HIDDEN)
+ || level >= TRIM_MEMORY_MODERATE) {
+ NativePageAssassin.getInstance().freezeAllHiddenPages();
+ }
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ Boolean result = KeyboardShortcuts.dispatchKeyEvent(event, this, mUIInitialized);
+ return result != null ? result : super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (!mUIInitialized) return false;
+ boolean isCurrentTabVisible = !mLayoutManager.overviewVisible()
+ && (!mIsTablet || getCurrentTabModel().getCount() != 0);
+ return KeyboardShortcuts.onKeyDown(event, this, isCurrentTabVisible, true)
+ || super.onKeyDown(keyCode, event);
+ }
+
+ private void hideMenus() {
+ if (mAppMenuHandler != null) mAppMenuHandler.hideAppMenu();
+ }
+
+ private ConnectionChangeReceiver getConnectionChangeReceiver() {
+ if (mConnectionChangeReceiver == null) {
+ mConnectionChangeReceiver = new ConnectionChangeReceiver();
+ }
+ return mConnectionChangeReceiver;
+ }
+
+ /**
+ * Sets the top margin of the control container.
+ *
+ * @param margin The new top margin of the control container.
+ */
+ @Override
+ public void setControlTopMargin(int margin) {
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)
+ mControlContainer.getLayoutParams();
+ lp.topMargin = margin;
+ mControlContainer.setLayoutParams(lp);
+ }
+
+ /**
+ * @return The top margin of the control container.
+ */
+ @Override
+ public int getControlTopMargin() {
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)
+ mControlContainer.getLayoutParams();
+ return lp.topMargin;
+ }
+
+ @Override
+ public void setActionBarBackgroundVisibility(boolean visible) {
+ int visibility = visible ? View.VISIBLE : View.GONE;
+ findViewById(R.id.action_bar_black_background).setVisibility(visibility);
+ }
+
+ @VisibleForTesting
+ public View getTabsView() {
+ return getCompositorViewHolder();
+ }
+
+ @VisibleForTesting
+ public LayoutManagerChrome getLayoutManager() {
+ return (LayoutManagerChrome) getCompositorViewHolder().getLayoutManager();
+ }
+
+ @VisibleForTesting
+ public Layout getOverviewListLayout() {
+ return getLayoutManager().getOverviewListLayout();
+ }
+
+ @Override
+ public boolean isOverlayVisible() {
+ return getCompositorViewHolder() != null && !getCompositorViewHolder().isTabInteractive();
+ }
+
+ @Override
+ public boolean mayShowUpdateInfoBar() {
+ return !isOverlayVisible();
+ }
+
+ // App Menu related code -----------------------------------------------------------------------
+
+ @Override
+ @VisibleForTesting
+ public AppMenuHandler getAppMenuHandler() {
+ return mAppMenuHandler;
+ }
+
+ @Override
+ public boolean shouldShowAppMenu() {
+ // The popup menu relies on the model created during the full UI initialization, so do not
+ // attempt to show the menu until the UI creation has finished.
+ if (!mUIInitialized) return false;
+
+ // Do not show the menu if we are in find in page view.
+ if (mFindToolbarManager != null && mFindToolbarManager.isShowing() && !mIsTablet) {
+ return false;
+ }
+
+ return super.shouldShowAppMenu();
+ }
+
+ private boolean showMenu() {
+ if (!mUIInitialized || isFullscreenVideoPlaying()) return false;
+
+ // The following will deduct the status bar height from the screen height and this
+ // is used as the height for the menu anchor. This fixes the bug where clicking
+ // inside a search box on google.com causes the menu to appear on top as keyboard
+ // reduces the view height.
+ int displayHeight = getResources().getDisplayMetrics().heightPixels;
+
+ Rect rect = new Rect();
+ this.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
+ int statusBarHeight = rect.top;
+ mMenuAnchor.setY((displayHeight - statusBarHeight));
+ mAppMenuHandler.showAppMenu(mMenuAnchor, true, false);
+ return true;
+ }
+
+ private boolean isFullscreenVideoPlaying() {
+ View view = ContentVideoView.getContentVideoView();
+ return view != null && view.getContext() == this;
+ }
+
+ @Override
+ public boolean isInOverviewMode() {
+ return mLayoutManager.overviewVisible();
+ }
+
+ @Override
+ public boolean hasDoneFirstDraw() {
+ return mToolbarHelper.hasDoneFirstDraw();
+ }
+
+ @Override
+ protected IntentHandlerDelegate createIntentHandlerDelegate() {
+ return new InternalIntentDelegate();
+ }
+
+ @Override
+ public void onOverviewModeStartedShowing(boolean showToolbar) {
+ if (mFindToolbarManager != null) mFindToolbarManager.hideToolbar();
+ }
+
+ @Override
+ public void onSceneChange(Layout layout) {
+ super.onSceneChange(layout);
+ if (!layout.shouldDisplayContentOverlay()) mTabModelSelectorImpl.onTabsViewShown();
+ }
+
+ @Override
+ public void onOverviewModeFinishedShowing() { }
+
+ @Override
+ public void onOverviewModeStartedHiding(boolean showToolbar, boolean delayAnimation) {}
+
+ @Override
+ public void onOverviewModeFinishedHiding() {}
+}

Powered by Google App Engine
This is Rietveld 408576698