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

Unified Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.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/document/DocumentActivity.java
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..a37ad16f1e37ba8afc6ba05f35ebea01c6906fc7
--- /dev/null
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.java
@@ -0,0 +1,991 @@
+// 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.document;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+
+import com.google.android.apps.chrome.R;
+
+import org.chromium.base.ActivityState;
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.SysUtils;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.ChromeMobileApplication;
+import org.chromium.chrome.browser.CompositorChromeActivity;
+import org.chromium.chrome.browser.IntentHandler;
+import org.chromium.chrome.browser.KeyboardShortcuts;
+import org.chromium.chrome.browser.Tab;
+import org.chromium.chrome.browser.TabState;
+import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.UrlUtilities;
+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.layouts.LayoutManagerDocument;
+import org.chromium.chrome.browser.document.DocumentTab.DocumentTabObserver;
+import org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkUtils;
+import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor;
+import org.chromium.chrome.browser.firstrun.FirstRunStatus;
+import org.chromium.chrome.browser.metrics.UmaUtils;
+import org.chromium.chrome.browser.ntp.DocumentNewTabPage;
+import org.chromium.chrome.browser.ntp.NewTabPage;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+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.ssl.ConnectionSecurityHelperSecurityLevel;
+import org.chromium.chrome.browser.tabmodel.SingleTabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
+import org.chromium.chrome.browser.tabmodel.document.ActivityDelegate;
+import org.chromium.chrome.browser.tabmodel.document.DocumentTabModel;
+import org.chromium.chrome.browser.tabmodel.document.DocumentTabModel.InitializationObserver;
+import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelImpl;
+import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelSelector;
+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.util.IntentUtils;
+import org.chromium.chrome.browser.widget.ControlContainer;
+import org.chromium.chrome.browser.widget.RoundedIconGenerator;
+import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager;
+import org.chromium.components.service_tab_launcher.ServiceTabLauncher;
+import org.chromium.content.browser.ContentVideoView;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.NavigationEntry;
+import org.chromium.content_public.browser.navigation_controller.LoadURLType;
+import org.chromium.content_public.common.Referrer;
+import org.chromium.ui.base.PageTransition;
+
+/**
+ * This is the main activity for Chrome while running in document mode. Each activity
+ * instance represents a single web page at a time while providing Chrome functionality.
+ *
+ * <p>
+ * Tab switching is handled via the system wide Android task management system.
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class DocumentActivity extends CompositorChromeActivity {
+ protected static final String KEY_INITIAL_URL = "DocumentActivity.KEY_INITIAL_URL";
+
+ private static final String TAG = "DocumentActivity";
+
+ private static final int APP_ICON_MIN_SIZE_DP = 32;
+ private static final int APP_ICON_SIZE_DP = 64;
+ private static final int APP_ICON_CORNER_RADIUS_DP = 3;
+ private static final int APP_ICON_TEXT_SIZE_DP = 30;
+ private static final int APP_ICON_DEFAULT_BACKGROUND_COLOR = Color.rgb(50, 50, 50);
+
+ // Animation exit duration defined in chrome/android/java/res/anim/menu_exit.xml as 150ms,
+ // plus add another 20ms for a re-layout.
+ private static final int MENU_EXIT_ANIMATION_WAIT_MS = 170;
+
+ private DocumentTabModel mTabModel;
+ private InitializationObserver mTabInitializationObserver;
+
+ private Bitmap mIcon;
+ // Indicates whether mIcon was generated by RoundedIconGenerator.
+ private boolean mIsUsingGeneratedIcon;
+
+ private Integer mThemeColor;
+ private int mDefaultThemeColor;
+
+ private DocumentTab mDocumentTab;
+ private ToolbarHelper mToolbarHelper;
+
+ private ChromeAppMenuPropertiesDelegate mChromeAppMenuPropertiesDelegate;
+ private AppMenuHandler mAppMenuHandler;
+ private RoundedIconGenerator mDocumentAppIconGenerator;
+ private FindToolbarManager mFindToolbarManager;
+ private boolean mRecordedStartupUma;
+
+ // Whether the page has started loading in this (browser) process once already. Must only be
+ // used from the UI thread.
+ private static boolean sIsFirstPageLoadStart = true;
+
+ /** Whether the DocumentTab has already been added to the TabModel. */
+ private boolean mNeedsToBeAddedToTabModel;
+
+ @Override
+ protected boolean isStartedUpCorrectly(Intent intent) {
+ int tabId = ActivityDelegate.getTabIdFromIntent(getIntent());
+ boolean isDocumentMode = FeatureUtilities.isDocumentMode(this);
+ boolean isStartedUpCorrectly = tabId != Tab.INVALID_TAB_ID && isDocumentMode;
+ if (!isStartedUpCorrectly) {
+ Log.e(TAG, "Discarding Intent: Tab = " + tabId + ", Document mode = " + isDocumentMode);
+ }
+ return isStartedUpCorrectly;
+ }
+
+ @Override
+ public void 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. See more details in ChromeTabbedActivity.preInflationStartup().
+ if (!LibraryLoader.isInitialized()) {
+ UmaUtils.setRunningApplicationStart(true);
+ }
+
+ DocumentTabModelSelector.setPrioritizedTabId(
+ ActivityDelegate.getTabIdFromIntent(getIntent()));
+
+ mTabModel = ChromeMobileApplication.getDocumentTabModelSelector().getModel(isIncognito());
+ mTabModel.startTabStateLoad();
+ updateLastTabId();
+
+ SingleTabModelSelector selector = new SingleTabModelSelector(this, isIncognito(), false) {
+ @Override
+ public Tab openNewTab(LoadUrlParams loadUrlParams, TabLaunchType type, Tab parent,
+ boolean incognito) {
+ PendingDocumentData params = null;
+ if (loadUrlParams.getPostData() != null
+ || loadUrlParams.getVerbatimHeaders() != null
+ || loadUrlParams.getReferrer() != null) {
+ params = new PendingDocumentData();
+ params.postData = loadUrlParams.getPostData();
+ params.extraHeaders = loadUrlParams.getVerbatimHeaders();
+ params.referrer = loadUrlParams.getReferrer();
+ }
+
+ int launchMode = type == TabLaunchType.FROM_LONGPRESS_BACKGROUND
+ ? ChromeLauncherActivity.LAUNCH_MODE_AFFILIATED
+ : ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND;
+ Activity parentActivity =
+ parent == null ? null : parent.getWindowAndroid().getActivity().get();
+ ChromeLauncherActivity.launchDocumentInstance(parentActivity, incognito,
+ launchMode, loadUrlParams.getUrl(),
+ DocumentMetricIds.STARTED_BY_WINDOW_OPEN,
+ loadUrlParams.getTransitionType(), false, params);
+ return null;
+ }
+ };
+ setTabModelSelector(selector);
+
+ super.preInflationStartup();
+
+ supportRequestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY);
+ }
+
+ @Override
+ protected int getControlContainerLayoutId() {
+ return R.layout.control_container;
+ }
+
+ @Override
+ public void postInflationStartup() {
+ super.postInflationStartup();
+
+ ToolbarControlContainer controlContainer =
+ ((ToolbarControlContainer) findViewById(R.id.control_container));
+ mChromeAppMenuPropertiesDelegate = new ChromeAppMenuPropertiesDelegate(this);
+ mAppMenuHandler = new AppMenuHandler(this, mChromeAppMenuPropertiesDelegate,
+ R.menu.main_menu);
+ mToolbarHelper = new ToolbarHelper(this, controlContainer, mAppMenuHandler,
+ mChromeAppMenuPropertiesDelegate, getCompositorViewHolder().getInvalidator());
+
+ final int tabId = ActivityDelegate.getTabIdFromIntent(getIntent());
+ mTabInitializationObserver = new InitializationObserver(mTabModel) {
+ @Override
+ public boolean isSatisfied(int currentState) {
+ return currentState >= DocumentTabModelImpl.STATE_LOAD_TAB_STATE_BG_END
+ || mTabModel.isTabStateReady(tabId);
+ }
+
+ @Override
+ public boolean isCanceled() {
+ return isDestroyed() || isFinishing();
+ }
+
+ @Override
+ public void runImmediately() {
+ initializeUI();
+ }
+ };
+ }
+
+ @Override
+ @VisibleForTesting
+ public AppMenuHandler getAppMenuHandler() {
+ return mAppMenuHandler;
+ }
+
+ @Override
+ public void prepareMenu(Menu menu) {
+ if (isNewTabPage() && !isIncognito()) {
+ menu.findItem(R.id.new_tab_menu_id).setVisible(false);
+ }
+ }
+
+ @Override
+ public void finishNativeInitialization() {
+ ChromePreferenceManager preferenceManager =
+ ChromePreferenceManager.getInstance(this);
+ if (isNewTabPage() && FirstRunStatus.getFirstRunFlowComplete(this)) {
+ // Only show promos on second NTP.
+ 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);
+ }
+ }
+
+ FirstRunSignInProcessor.start(this);
+
+ if (!preferenceManager.hasAttemptedMigrationOnUpgrade()) {
+ InitializationObserver observer = new InitializationObserver(
+ ChromeMobileApplication.getDocumentTabModelSelector().getModel(false)) {
+ @Override
+ protected void runImmediately() {
+ DocumentMigrationHelper.migrateTabsToDocumentForUpgrade(DocumentActivity.this,
+ DocumentMigrationHelper.FINALIZE_MODE_NO_ACTION);
+ }
+
+ @Override
+ public boolean isSatisfied(int currentState) {
+ return currentState == DocumentTabModelImpl.STATE_FULLY_LOADED;
+ }
+
+ @Override
+ public boolean isCanceled() {
+ return false;
+ }
+ };
+
+ observer.runWhenReady();
+ }
+
+ getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
+ Window.PROGRESS_VISIBILITY_OFF);
+
+ // Initialize the native-side TabModels so that the Profile is available when necessary.
+ ChromeMobileApplication.getDocumentTabModelSelector().onNativeLibraryReady();
+ mTabInitializationObserver.runWhenReady();
+
+ if (mNeedsToBeAddedToTabModel) {
+ mNeedsToBeAddedToTabModel = false;
+ mTabModel.addTab(getIntent(), mDocumentTab);
+ getTabModelSelector().setTab(mDocumentTab);
+ }
+
+ super.finishNativeInitialization();
+ }
+
+ @Override
+ protected void onDeferredStartup() {
+ super.onDeferredStartup();
+ mToolbarHelper.onDeferredStartup();
+ }
+
+ @Override
+ public boolean hasDoneFirstDraw() {
+ return mToolbarHelper.hasDoneFirstDraw();
+ }
+
+ /**
+ * @return The ID of the Tab.
+ */
+ protected final int determineTabId() {
+ int tabId = ActivityDelegate.getTabIdFromIntent(getIntent());
+ if (tabId == Tab.INVALID_TAB_ID && mDocumentTab != null) tabId = mDocumentTab.getId();
+ return tabId;
+ }
+
+ /**
+ * Determine the last known URL that this Document was displaying when it was stopped.
+ * @return URL that the user was last known to have seen, or null if one couldn't be found.
+ */
+ private String determineLastKnownUrl() {
+ int tabId = determineTabId();
+ String url = mTabModel.getCurrentUrlForDocument(tabId);
+ if (TextUtils.isEmpty(url)) url = determineInitialUrl(tabId);
+ return url;
+ }
+
+ /**
+ * Determine the URL that this Document was initially opened for. It can be stashed in multiple
+ * locations because IncognitoDocumentActivities are disallowed from passing URLs in the Intent.
+ * @param tabId ID of the Tab owned by this Activity.
+ * @return Initial URL, or null if it couldn't be determined.
+ */
+ protected String determineInitialUrl(int tabId) {
+ String initialUrl = null;
+
+ // Check if the TabModel has it.
+ if (mTabModel != null) {
+ initialUrl = mTabModel.getInitialUrlForDocument(tabId);
+ }
+
+ // Check if the URL was passed through the Intent.
+ if (TextUtils.isEmpty(initialUrl) && getIntent() != null) {
+ initialUrl = IntentHandler.getUrlFromIntent(getIntent());
+ }
+
+ // Check the Tab's history.
+ if (TextUtils.isEmpty(initialUrl) && mDocumentTab != null
+ && mDocumentTab.getWebContents() != null) {
+ NavigationEntry entry =
+ mDocumentTab.getWebContents().getNavigationController().getEntryAtIndex(0);
+ if (entry != null) initialUrl = entry.getOriginalUrl();
+ }
+
+ return initialUrl;
+ }
+
+ @Override
+ public CharSequence onCreateDescription() {
+ return mDocumentTab != null ? mDocumentTab.getTitle() : "";
+ }
+
+ @Override
+ public final DocumentTab getActivityTab() {
+ return mDocumentTab;
+ }
+
+ @Override
+ public void onStartWithNative() {
+ super.onStartWithNative();
+ handleDocumentUma();
+ ChromeLauncherActivity.sendExceptionCount();
+ }
+
+ private void handleDocumentUma() {
+ if (mRecordedStartupUma) {
+ DocumentUma.recordStartedBy(
+ DocumentMetricIds.STARTED_BY_ACTIVITY_BROUGHT_TO_FOREGROUND);
+ } else {
+ mRecordedStartupUma = true;
+
+ if (getSavedInstanceState() == null) {
+ DocumentUma.recordStartedBy(getPackageName(), getIntent());
+ } else {
+ DocumentUma.recordStartedBy(DocumentMetricIds.STARTED_BY_ACTIVITY_RESTARTED);
+ }
+ }
+ DocumentUma.recordInDocumentMode(true);
+ }
+
+ @Override
+ public void onPause() {
+ // If finishing, release all the active media players as we don't know when onStop()
+ // will get called.
+ super.onPause();
+ if (isFinishing() && mDocumentTab != null && mDocumentTab.getWebContents() != null) {
+ mDocumentTab.getWebContents().releaseMediaPlayers();
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ if (hasWindowFocus) updateLastTabId();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (getIntent() != null) {
+ DocumentUtils.finishOtherTasksWithTabID(
+ ActivityDelegate.getTabIdFromIntent(getIntent()), getTaskId());
+ }
+ }
+
+ @Override
+ public void onResumeWithNative() {
+ super.onResumeWithNative();
+
+ if (mDocumentTab != null) {
+ PendingDocumentData pendingData = ChromeMobileApplication.getDocumentTabModelSelector()
+ .removePendingDocumentData(ActivityDelegate.getTabIdFromIntent(getIntent()));
+ if (pendingData != null && pendingData.url != null) {
+ loadLastKnownUrl(pendingData);
+ }
+ mDocumentTab.show(TabSelectionType.FROM_USER);
+ }
+ }
+
+ @Override
+ protected void onDestroyInternal() {
+ if (mToolbarHelper != null) mToolbarHelper.destroy();
+
+ super.onDestroyInternal();
+ }
+
+ @Override
+ public void onStopWithNative() {
+ if (mDocumentTab != null) {
+ mDocumentTab.setShouldPreserve(!IntentUtils.safeGetBooleanExtra(getIntent(),
+ IntentHandler.EXTRA_APPEND_TASK, false));
+ mTabModel.updateEntry(getIntent(), mDocumentTab);
+ }
+ if (mAppMenuHandler != null) mAppMenuHandler.hideAppMenu();
+ super.onStopWithNative();
+ }
+
+ @Override
+ public SingleTabModelSelector getTabModelSelector() {
+ return (SingleTabModelSelector) super.getTabModelSelector();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (mAppMenuHandler != null) mAppMenuHandler.hideAppMenu();
+ super.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ public void onOrientationChange(int orientation) {
+ super.onOrientationChange(orientation);
+ mToolbarHelper.onOrientationChange();
+ }
+
+ @Override
+ protected void onAccessibilityModeChanged(boolean enabled) {
+ super.onAccessibilityModeChanged(enabled);
+ mToolbarHelper.onAccessibilityStatusChanged(enabled);
+ }
+
+ private void loadLastKnownUrl(PendingDocumentData pendingData) {
+ Intent intent = getIntent();
+ if (pendingData != null && pendingData.originalIntent != null) {
+ intent = pendingData.originalIntent;
+ }
+ boolean isIntentChromeOrFirstParty = IntentHandler.isIntentChromeOrFirstParty(intent,
+ getApplicationContext());
+ int transitionType = intent == null ? PageTransition.LINK
+ : IntentUtils.safeGetIntExtra(intent, IntentHandler.EXTRA_PAGE_TRANSITION_TYPE,
+ PageTransition.LINK);
+ if ((transitionType != PageTransition.TYPED)
+ && (transitionType != PageTransition.LINK)
+ && !isIntentChromeOrFirstParty) {
+ // Only 1st party authenticated applications can set the transition type to be
+ // anything other than TYPED for security reasons.
+ transitionType = PageTransition.LINK;
+ }
+
+ if (transitionType == PageTransition.LINK) {
+ transitionType |= PageTransition.FROM_API;
+ }
+
+ String url = (pendingData != null && pendingData.url != null) ? pendingData.url :
+ determineLastKnownUrl();
+
+ LoadUrlParams loadUrlParams = new LoadUrlParams(url, transitionType);
+ if (getIntent() != null) {
+ loadUrlParams.setIntentReceivedTimestamp(getOnCreateTimestampUptimeMs());
+ }
+
+ Uri referrerExtra = null;
+ if (isIntentChromeOrFirstParty
+ && IntentUtils.safeGetParcelableExtra(intent, Intent.EXTRA_REFERRER) != null) {
+ referrerExtra = (Uri) IntentUtils.safeGetParcelableExtra(intent, Intent.EXTRA_REFERRER);
+ loadUrlParams.setReferrer(new Referrer(
+ referrerExtra.toString(), 1 /* WebReferrerPolicyDefault */));
+ }
+
+ if (pendingData != null) {
+ if (pendingData.postData != null) {
+ loadUrlParams.setPostData(pendingData.postData);
+ loadUrlParams.setLoadType(LoadURLType.BROWSER_INITIATED_HTTP_POST);
+ }
+ loadUrlParams.setVerbatimHeaders(pendingData.extraHeaders);
+ loadUrlParams.setReferrer(pendingData.referrer);
+ mDocumentTab.getTabRedirectHandler().updateIntent(pendingData.originalIntent);
+ } else {
+ if (getIntent() != null) {
+ try {
+ intent = IntentUtils.safeGetParcelableExtra(getIntent(),
+ IntentHandler.EXTRA_ORIGINAL_INTENT);
+ } catch (Throwable t) {
+ // Ignore exception.
+ }
+ }
+ mDocumentTab.getTabRedirectHandler().updateIntent(intent);
+ }
+
+ mDocumentTab.loadUrl(loadUrlParams);
+ if (pendingData != null && pendingData.requestId > 0) {
+ ServiceTabLauncher.onWebContentsForRequestAvailable(
+ pendingData.requestId, mDocumentTab.getWebContents());
+ }
+
+ if (getIntent() != null && IntentUtils.safeGetBooleanExtra(getIntent(),
+ IntentHandler.EXTRA_USE_DESKTOP_USER_AGENT, false)) {
+ // The desktop user agent can't be carried over without the Tab already having a URL,
+ // so we're forced to set the user agent and then reload the page.
+ mDocumentTab.setUseDesktopUserAgent(true, true);
+ }
+ }
+
+ @Override
+ public void terminateIncognitoSession() {
+ ChromeMobileApplication.getDocumentTabModelSelector().getModel(true).closeAllTabs();
+ }
+
+ private void initializeUI() {
+ mDefaultThemeColor = isIncognito()
+ ? getResources().getColor(R.color.incognito_primary_color)
+ : getResources().getColor(R.color.default_primary_color);
+ PendingDocumentData pendingData = ChromeMobileApplication.getDocumentTabModelSelector()
+ .removePendingDocumentData(ActivityDelegate.getTabIdFromIntent(getIntent()));
+ int tabId = determineTabId();
+ TabState tabState = mTabModel.getTabStateForDocument(tabId);
+ mDocumentTab = DocumentTab.create(DocumentActivity.this, isIncognito(),
+ getWindowAndroid(), determineLastKnownUrl(),
+ pendingData != null ? pendingData.webContents : null,
+ pendingData != null ? pendingData.webContentsPaused : false, tabState);
+ if (mTabModel.isNativeInitialized()) {
+ mTabModel.addTab(getIntent(), mDocumentTab);
+ } else {
+ mNeedsToBeAddedToTabModel = true;
+ }
+
+ mAppMenuHandler.addObserver(new AppMenuObserver() {
+ @Override
+ public void onMenuVisibilityChanged(boolean isVisible) {
+ if (!isVisible) {
+ mChromeAppMenuPropertiesDelegate.onMenuDismissed();
+ }
+ }
+ });
+
+ getTabModelSelector().setTab(mDocumentTab);
+
+ if (!mDocumentTab.didRestoreState() || (pendingData != null && pendingData.url != null)) {
+ if (!mDocumentTab.isCreatedWithWebContents()) {
+ // Don't load tabs in the background on low end devices. We will call
+ // loadLastKnownUrl() in onResumeWithNative() next time activity is resumed.
+ int launchMode = IntentUtils.safeGetIntExtra(getIntent(),
+ ChromeLauncherActivity.EXTRA_LAUNCH_MODE,
+ ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND);
+ if (SysUtils.isLowEndDevice()
+ && launchMode == ChromeLauncherActivity.LAUNCH_MODE_AFFILIATED
+ && pendingData != null) {
+ // onResumeWithNative() wants pendingData.url to be non-null
+ if (pendingData.url == null) {
+ pendingData.url = determineLastKnownUrl();
+ }
+ ChromeMobileApplication.getDocumentTabModelSelector().addPendingDocumentData(
+ ActivityDelegate.getTabIdFromIntent(getIntent()), pendingData);
+ // Use the URL as the document title until tab is loaded
+ updateTaskDescription(pendingData.url, null);
+ } else {
+ loadLastKnownUrl(pendingData);
+ }
+ }
+ mDocumentTab.setShouldPreserve(IntentUtils.safeGetBooleanExtra(getIntent(),
+ IntentHandler.EXTRA_PRESERVE_TASK, false));
+ }
+
+ ToolbarControlContainer controlContainer =
+ (ToolbarControlContainer) findViewById(R.id.control_container);
+ LayoutManagerDocument layoutDriver = new LayoutManagerDocument(getCompositorViewHolder());
+ initializeCompositorContent(layoutDriver, findViewById(R.id.url_bar),
+ (ViewGroup) findViewById(android.R.id.content), controlContainer);
+
+ mFindToolbarManager = new FindToolbarManager(this, getTabModelSelector(),
+ mToolbarHelper.getContextualMenuBar()
+ .getCustomSelectionActionModeCallback());
+ controlContainer.setFindToolbarManager(mFindToolbarManager);
+ if (getContextualSearchManager() != null) {
+ getContextualSearchManager().setFindToolbarManager(mFindToolbarManager);
+ }
+
+ mToolbarHelper.initializeControls(
+ mFindToolbarManager, null, layoutDriver, null, null, null, null);
+
+ mDocumentTab.setFullscreenManager(getFullscreenManager());
+
+ mDocumentTab.addObserver(new DocumentTabObserver() {
+ @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 (!sIsFirstPageLoadStart) {
+ UmaUtils.setRunningApplicationStart(false);
+ } else {
+ sIsFirstPageLoadStart = false;
+ }
+ }
+
+ @Override
+ public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) {
+ if (!didStartLoad) return;
+ mThemeColor = tab.getWebContents().getThemeColor(mDefaultThemeColor);
+ mIcon = null;
+ updateTaskDescription();
+ }
+
+ @Override
+ protected void onFaviconReceived(Bitmap image) {
+ super.onFaviconReceived(image);
+ int newHeight = image.getHeight();
+ int newWidth = image.getWidth();
+ int minSize =
+ (int) getResources().getDisplayMetrics().density * APP_ICON_MIN_SIZE_DP;
+ if (newHeight < minSize || newWidth < minSize) return;
+ if (mIcon != null && !mIsUsingGeneratedIcon && mIcon.getWidth() >= newWidth
+ && mIcon.getHeight() >= newHeight) {
+ return;
+ }
+ mIcon = image;
+ mIsUsingGeneratedIcon = false;
+ updateTaskDescription();
+ }
+
+ @Override
+ public void onUrlUpdated(Tab tab) {
+ assert mDocumentTab == tab;
+
+ updateTaskDescription();
+ mTabModel.updateEntry(getIntent(), mDocumentTab);
+ }
+
+ @Override
+ public void onTitleUpdated(Tab tab) {
+ super.onTitleUpdated(tab);
+ updateTaskDescription();
+ }
+
+ @Override
+ public void onSSLStateUpdated(Tab tab) {
+ int securityLevel = tab.getSecurityLevel();
+ if (securityLevel == ConnectionSecurityHelperSecurityLevel.SECURITY_ERROR
+ || securityLevel == ConnectionSecurityHelperSecurityLevel.SECURITY_WARNING
+ || securityLevel
+ == ConnectionSecurityHelperSecurityLevel.SECURITY_POLICY_WARNING) {
+ resetThemeColorAndIcon();
+ }
+ }
+
+ @Override
+ public void onDidNavigateMainFrame(Tab tab, String url, String baseUrl,
+ boolean isNavigationToDifferentPage, boolean isFragmentNavigation,
+ int statusCode) {
+ if (!isNavigationToDifferentPage) return;
+ mIcon = null;
+ }
+
+ @Override
+ public void onLoadStopped(Tab tab) {
+ assert mDocumentTab == tab;
+
+ updateTaskDescription();
+ mTabModel.updateEntry(getIntent(), mDocumentTab);
+ }
+
+ @Override
+ public void onDidChangeThemeColor(int color) {
+ if (color == Color.TRANSPARENT) color = mDefaultThemeColor;
+
+ // Ignore any transparency value.
+ color |= 0xFF000000;
+
+ mThemeColor = Integer.valueOf(color);
+ updateTaskDescription();
+ }
+
+ @Override
+ public void onDidAttachInterstitialPage(Tab tab) {
+ resetThemeColorAndIcon();
+ }
+
+ @Override
+ public void onDidDetachInterstitialPage(Tab tab) {
+ mThemeColor = tab.getWebContents().getThemeColor(mDefaultThemeColor);
+ mIcon = null;
+ updateTaskDescription();
+ }
+
+ @Override
+ public void onCrash(Tab tab, boolean sadTabShown) {
+ int currentState = ApplicationStatus.getStateForActivity(DocumentActivity.this);
+ if (currentState != ActivityState.STOPPED) return;
+
+ if (!isTaskRoot() || IntentUtils.safeGetBooleanExtra(getIntent(),
+ IntentHandler.EXTRA_APPEND_TASK, false)) {
+ return;
+ }
+
+ // Finishing backgrounded Activities whose renderers have crashed allows us to
+ // destroy them and return resources sooner than if we wait for Android to destroy
+ // the Activities themselves. Problematically, this also removes
+ // IncognitoDocumentActivity instances from Android's Recents menu and auto-closes
+ // the tab. Instead, take a hit and keep the Activities alive -- Android will
+ // eventually destroy the Activities, anyway (crbug.com/450292).
+ if (!isIncognito()) finish();
+ }
+
+ @Override
+ public void onSetCoveredByChildActivity() {
+ mTabModel.updateEntry(getIntent(), mDocumentTab);
+ }
+ });
+
+ removeWindowBackground();
+
+ if (mDocumentTab != null) {
+ BandwidthReductionPreferences.launchDataReductionSSLInfoBar(
+ DocumentActivity.this, mDocumentTab.getWebContents());
+ }
+ }
+
+ private void resetThemeColorAndIcon() {
+ mThemeColor = null;
+ mIcon = null;
+ updateTaskDescription();
+ }
+
+ private void updateLastTabId() {
+ ChromeMobileApplication.getDocumentTabModelSelector().selectModel(isIncognito());
+ int tabId = mDocumentTab == null
+ ? ActivityDelegate.getTabIdFromIntent(getIntent()) : mDocumentTab.getId();
+ mTabModel.setLastShownId(tabId);
+ }
+
+ @Override
+ public boolean handleBackPressed() {
+ if (mDocumentTab == null) return false;
+
+ View view = ContentVideoView.getContentVideoView();
+ if (view != null && view.getContext() == this) {
+ ContentVideoView.getContentVideoView().exitFullscreen(false);
+ return true;
+ }
+
+ if (getFullscreenManager().getPersistentFullscreenMode()) {
+ getFullscreenManager().setPersistentFullscreenMode(false);
+ return true;
+ }
+
+ if (mDocumentTab.canGoBack()) {
+ mDocumentTab.goBack();
+ } else if (!mDocumentTab.shouldPreserve()) {
+ finishAndRemoveTask();
+ } else {
+ moveTaskToBack(true);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean createContextualSearchTab(ContentViewCore searchContentViewCore) {
+ NavigationEntry entry =
+ searchContentViewCore.getWebContents().getNavigationController().getPendingEntry();
+ String url = entry != null
+ ? entry.getUrl() : searchContentViewCore.getWebContents().getUrl();
+ PendingDocumentData documentData = new PendingDocumentData();
+ ChromeLauncherActivity.launchDocumentInstance(this, false,
+ ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND, url,
+ DocumentMetricIds.STARTED_BY_CONTEXTUAL_SEARCH,
+ PageTransition.LINK, false, documentData);
+ return false;
+ }
+
+ @Override
+ public boolean onMenuOrKeyboardAction(int id, boolean fromMenu) {
+ if (id == R.id.new_tab_menu_id) {
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ launchNtp(false);
+ }
+ }, MENU_EXIT_ANIMATION_WAIT_MS);
+ RecordUserAction.record("MobileMenuNewTab");
+ RecordUserAction.record("MobileNewTabOpened");
+ } else if (id == R.id.new_incognito_tab_menu_id) {
+ // 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");
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ launchNtp(true);
+ }
+ }, MENU_EXIT_ANIMATION_WAIT_MS);
+ } else if (id == R.id.all_bookmarks_menu_id) {
+ if (!EnhancedBookmarkUtils.showEnhancedBookmarkIfEnabled(this)) {
+ DocumentNewTabPage.launchBookmarksDialog(this, mDocumentTab, getTabModelSelector());
+ }
+ RecordUserAction.record("MobileMenuAllBookmarks");
+ } else if (id == R.id.recent_tabs_menu_id) {
+ DocumentNewTabPage.launchRecentTabsDialog(this, mDocumentTab, false);
+ RecordUserAction.record("MobileMenuOpenTabs");
+ } 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) {
+ if (mToolbarHelper.isInitialized()) {
+ mAppMenuHandler.showAppMenu(mToolbarHelper.getMenuAnchor(), true,
+ false);
+ }
+ } else if (id == R.id.focus_url_bar) {
+ if (mToolbarHelper.isInitialized()) mToolbarHelper.setUrlBarFocus(true);
+ } else {
+ return super.onMenuOrKeyboardAction(id, fromMenu);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ Boolean result = KeyboardShortcuts.dispatchKeyEvent(event, this,
+ mToolbarHelper.isInitialized());
+ return result != null ? result : super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (!mToolbarHelper.isInitialized()) return false;
+ return KeyboardShortcuts.onKeyDown(event, this, true, false)
+ || super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean shouldShowAppMenu() {
+ if (mDocumentTab == null || !mToolbarHelper.isInitialized()) {
+ return false;
+ }
+
+ return super.shouldShowAppMenu();
+ }
+
+ private void updateTaskDescription() {
+ if (mDocumentTab == null) {
+ updateTaskDescription(null, null);
+ return;
+ }
+
+ if (isNewTabPage() && !isIncognito()) {
+ // NTP needs a new color in recents, but uses the default application title and icon;
+ updateTaskDescription(null, null);
+ return;
+ }
+
+ String label = mDocumentTab.getTitle();
+ String domain = UrlUtilities.getDomainAndRegistry(mDocumentTab.getUrl(), false);
+ if (TextUtils.isEmpty(label)) {
+ label = domain;
+ }
+ if (mIcon == null && TextUtils.isEmpty(label)) {
+ updateTaskDescription(null, null);
+ return;
+ }
+
+ if (isIncognito()) {
+ mIcon = null;
+ } else if (mIcon == null) {
+ if (mDocumentAppIconGenerator == null) {
+ mDocumentAppIconGenerator = new RoundedIconGenerator(this,
+ APP_ICON_SIZE_DP, APP_ICON_SIZE_DP,
+ APP_ICON_CORNER_RADIUS_DP, APP_ICON_DEFAULT_BACKGROUND_COLOR,
+ APP_ICON_TEXT_SIZE_DP);
+ }
+ mIcon = mDocumentAppIconGenerator.generateIconForUrl(mDocumentTab.getUrl());
+ mIsUsingGeneratedIcon = true;
+ }
+
+ updateTaskDescription(label, mIcon);
+ }
+
+ protected int getThemeColor() {
+ if (isIncognito()) {
+ return mDefaultThemeColor;
+ } else {
+ return mThemeColor != null ? mThemeColor.intValue() : mDefaultThemeColor;
+ }
+ }
+
+ private boolean shouldUseDefaultStatusBarColor() {
+ return isIncognito() || mThemeColor == null || mThemeColor == mDefaultThemeColor;
+ }
+
+ protected void updateTaskDescription(String label, Bitmap icon) {
+ int color = getThemeColor();
+ DocumentUtils.updateTaskDescription(this, label, icon, color,
+ shouldUseDefaultStatusBarColor());
+ mToolbarHelper.setThemeColor(color);
+
+ ControlContainer controlContainer =
+ (ControlContainer) findViewById(R.id.control_container);
+ controlContainer.getToolbarResourceAdapter().invalidate(null);
+ }
+
+ /**
+ * @return Whether the {@link DocumentTab} this activity uses is incognito.
+ */
+ protected boolean isIncognito() {
+ return false;
+ }
+
+ /**
+ * @return Whether this activity contains a tab that is showing the new tab page.
+ */
+ boolean isNewTabPage() {
+ String url;
+ if (mDocumentTab == null) {
+ // If the Tab hasn't been created yet, then we're really early in initialization.
+ // Use a combination of the original URL from the Intent and whether or not the Tab is
+ // retargetable to know whether or not the user navigated away from the NTP.
+ // If the Entry doesn't exist, then the DocumentActivity never got a chance to add
+ // itself to the TabList and is likely to be retargetable.
+ int tabId = ActivityDelegate.getTabIdFromIntent(getIntent());
+ url = IntentHandler.getUrlFromIntent(getIntent());
+ if (mTabModel.hasEntryForTabId(tabId) && !mTabModel.isRetargetable(tabId)) return false;
+ } else {
+ url = mDocumentTab.getUrl();
+ }
+ return NewTabPage.isNTPUrl(url);
+ }
+
+ /**
+ * Determines whether the given class can be classified as a DocumentActivity (this includes
+ * both regular document activity and incognito document activity).
+ * @param className The class name to inspect.
+ * @return Whether the className is that of a document activity.
+ */
+ public static boolean isDocumentActivity(String className) {
+ return TextUtils.equals(className, IncognitoDocumentActivity.class.getName())
+ || TextUtils.equals(className, DocumentActivity.class.getName());
+ }
+
+ /**
+ * Launch a new DocumentActivity showing the new tab page.
+ * @param incognito Whether the new NTP should be in incognito mode.
+ */
+ private void launchNtp(boolean incognito) {
+ if (incognito && !PrefServiceBridge.getInstance().isIncognitoModeEnabled()) return;
+ ChromeLauncherActivity.launchDocumentInstance(this, incognito,
+ ChromeLauncherActivity.LAUNCH_MODE_RETARGET, UrlConstants.NTP_URL,
+ DocumentMetricIds.STARTED_BY_OPTIONS_MENU, PageTransition.AUTO_TOPLEVEL, false,
+ null);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698