| 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() {}
|
| +}
|
|
|