| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.document; | 5 package org.chromium.chrome.browser.document; |
| 6 | 6 |
| 7 import android.annotation.TargetApi; | 7 import android.annotation.TargetApi; |
| 8 import android.content.Intent; | 8 import android.content.Intent; |
| 9 import android.graphics.Color; | |
| 10 import android.os.Build; | 9 import android.os.Build; |
| 11 import android.text.TextUtils; | 10 import android.text.TextUtils; |
| 12 import android.view.KeyEvent; | |
| 13 import android.view.Menu; | |
| 14 import android.view.View; | |
| 15 import android.view.View.OnClickListener; | |
| 16 import android.view.ViewGroup; | |
| 17 import android.view.Window; | |
| 18 | 11 |
| 19 import org.chromium.base.ActivityState; | |
| 20 import org.chromium.base.ApiCompatibilityUtils; | |
| 21 import org.chromium.base.ApplicationStatus; | |
| 22 import org.chromium.base.Log; | 12 import org.chromium.base.Log; |
| 23 import org.chromium.base.SysUtils; | |
| 24 import org.chromium.base.library_loader.LibraryLoader; | |
| 25 import org.chromium.base.metrics.RecordUserAction; | |
| 26 import org.chromium.chrome.R; | |
| 27 import org.chromium.chrome.browser.ActivityTabTaskDescriptionHelper; | |
| 28 import org.chromium.chrome.browser.ChromeActivity; | 13 import org.chromium.chrome.browser.ChromeActivity; |
| 29 import org.chromium.chrome.browser.ChromeApplication; | |
| 30 import org.chromium.chrome.browser.IntentHandler; | |
| 31 import org.chromium.chrome.browser.KeyboardShortcuts; | |
| 32 import org.chromium.chrome.browser.TabState; | |
| 33 import org.chromium.chrome.browser.UrlConstants; | |
| 34 import org.chromium.chrome.browser.bookmarks.BookmarkUtils; | |
| 35 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChange
Reason; | |
| 36 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerDocument; | |
| 37 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerDocumentTabSw
itcher; | |
| 38 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; | |
| 39 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior.Overv
iewModeObserver; | |
| 40 import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor; | |
| 41 import org.chromium.chrome.browser.firstrun.FirstRunStatus; | |
| 42 import org.chromium.chrome.browser.metrics.StartupMetrics; | |
| 43 import org.chromium.chrome.browser.metrics.UmaUtils; | |
| 44 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; | |
| 45 import org.chromium.chrome.browser.ntp.NewTabPage; | |
| 46 import org.chromium.chrome.browser.preferences.ChromePreferenceManager; | |
| 47 import org.chromium.chrome.browser.preferences.PrefServiceBridge; | |
| 48 import org.chromium.chrome.browser.preferences.datareduction.DataReductionPromoS
creen; | |
| 49 import org.chromium.chrome.browser.signin.SigninPromoUtil; | |
| 50 import org.chromium.chrome.browser.tab.EmptyTabObserver; | |
| 51 import org.chromium.chrome.browser.tab.Tab; | 14 import org.chromium.chrome.browser.tab.Tab; |
| 52 import org.chromium.chrome.browser.tab.TabUma.TabCreationState; | |
| 53 import org.chromium.chrome.browser.tabmodel.AsyncTabParams; | |
| 54 import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager; | |
| 55 import org.chromium.chrome.browser.tabmodel.SingleTabModelSelector; | |
| 56 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; | |
| 57 import org.chromium.chrome.browser.tabmodel.TabReparentingParams; | |
| 58 import org.chromium.chrome.browser.tabmodel.document.ActivityDelegate; | 15 import org.chromium.chrome.browser.tabmodel.document.ActivityDelegate; |
| 59 import org.chromium.chrome.browser.tabmodel.document.AsyncTabCreationParams; | |
| 60 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModel; | |
| 61 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModel.Initializa
tionObserver; | |
| 62 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelImpl; | |
| 63 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelSelector; | |
| 64 import org.chromium.chrome.browser.tabmodel.document.TabDelegate; | |
| 65 import org.chromium.chrome.browser.toolbar.ToolbarControlContainer; | |
| 66 import org.chromium.chrome.browser.util.FeatureUtilities; | |
| 67 import org.chromium.chrome.browser.util.IntentUtils; | |
| 68 import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager; | |
| 69 import org.chromium.components.service_tab_launcher.ServiceTabLauncher; | |
| 70 import org.chromium.content_public.browser.LoadUrlParams; | |
| 71 import org.chromium.content_public.browser.NavigationEntry; | |
| 72 import org.chromium.content_public.browser.WebContents; | |
| 73 import org.chromium.ui.base.PageTransition; | |
| 74 | 16 |
| 75 /** | 17 /** |
| 76 * This is the main activity for Chrome while running in document mode. Each ac
tivity | 18 * Deprected class for running Chrome in document mode. Kept around to force us
ers into the correct |
| 77 * instance represents a single web page at a time while providing Chrome functi
onality. | 19 * {@link Activity}. |
| 78 * | |
| 79 * <p> | |
| 80 * Tab switching is handled via the system wide Android task management system. | |
| 81 */ | 20 */ |
| 82 @TargetApi(Build.VERSION_CODES.LOLLIPOP) | 21 @TargetApi(Build.VERSION_CODES.LOLLIPOP) |
| 83 public class DocumentActivity extends ChromeActivity { | 22 public class DocumentActivity extends ChromeActivity { |
| 84 // Legacy class names to match Chrome pre-44 activity names. See crbug.com/5
03807 | 23 // Legacy class names to match Chrome pre-44 activity names. See crbug.com/5
03807 |
| 85 public static final String LEGACY_CLASS_NAME = | 24 public static final String LEGACY_CLASS_NAME = |
| 86 "com.google.android.apps.chrome.document.DocumentActivity"; | 25 "com.google.android.apps.chrome.document.DocumentActivity"; |
| 87 public static final String LEGACY_INCOGNITO_CLASS_NAME = | 26 public static final String LEGACY_INCOGNITO_CLASS_NAME = |
| 88 "com.google.android.apps.chrome.document.IncognitoDocumentActivity"; | 27 "com.google.android.apps.chrome.document.IncognitoDocumentActivity"; |
| 89 | 28 |
| 90 protected static final String KEY_INITIAL_URL = "DocumentActivity.KEY_INITIA
L_URL"; | |
| 91 | |
| 92 private static final String TAG = "DocumentActivity"; | 29 private static final String TAG = "DocumentActivity"; |
| 93 | 30 |
| 94 // Animation exit duration defined in chrome/android/java/res/anim/menu_exit
.xml as 150ms, | |
| 95 // plus add another 20ms for a re-layout. | |
| 96 private static final int MENU_EXIT_ANIMATION_WAIT_MS = 170; | |
| 97 | |
| 98 private class DocumentTabObserver extends EmptyTabObserver { | |
| 99 @Override | |
| 100 public void onPageLoadStarted(Tab tab, String url) { | |
| 101 // Discard startup navigation measurements when the user interfered
and started the | |
| 102 // 2nd navigation (in activity lifetime) in parallel. | |
| 103 if (!sIsFirstPageLoadStart) { | |
| 104 UmaUtils.setRunningApplicationStart(false); | |
| 105 } else { | |
| 106 sIsFirstPageLoadStart = false; | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 @Override | |
| 111 public void onUrlUpdated(Tab tab) { | |
| 112 assert mTab == tab; | |
| 113 mTabModel.updateEntry(getIntent(), mTab); | |
| 114 } | |
| 115 | |
| 116 @Override | |
| 117 public void onLoadStopped(Tab tab, boolean toDifferentDocument) { | |
| 118 assert mTab == tab; | |
| 119 mTabModel.updateEntry(getIntent(), mTab); | |
| 120 } | |
| 121 | |
| 122 @Override | |
| 123 public void onCrash(Tab tab, boolean sadTabShown) { | |
| 124 int currentState = ApplicationStatus.getStateForActivity(DocumentAct
ivity.this); | |
| 125 if (currentState != ActivityState.STOPPED) return; | |
| 126 | |
| 127 if (!isTaskRoot() || IntentUtils.safeGetBooleanExtra(getIntent(), | |
| 128 IntentHandler.EXTRA_APPEND_TASK, false)) { | |
| 129 return; | |
| 130 } | |
| 131 | |
| 132 // Finishing backgrounded Activities whose renderers have crashed al
lows us to | |
| 133 // destroy them and return resources sooner than if we wait for Andr
oid to destroy | |
| 134 // the Activities themselves. Problematically, this also removes | |
| 135 // IncognitoDocumentActivity instances from Android's Recents menu a
nd auto-closes | |
| 136 // the tab. Instead, take a hit and keep the Activities alive -- An
droid will | |
| 137 // eventually destroy the Activities, anyway (crbug.com/450292). | |
| 138 if (!isIncognito()) finish(); | |
| 139 } | |
| 140 } | |
| 141 private DocumentTabModel mTabModel; | |
| 142 private InitializationObserver mTabInitializationObserver; | |
| 143 | |
| 144 private ActivityTabTaskDescriptionHelper mTaskDescriptionHelper; | |
| 145 | |
| 146 private Tab mTab; | |
| 147 private FindToolbarManager mFindToolbarManager; | |
| 148 private boolean mRecordedStartupUma; | |
| 149 | |
| 150 // Whether the page has started loading in this (browser) process once alrea
dy. Must only be | |
| 151 // used from the UI thread. | |
| 152 private static boolean sIsFirstPageLoadStart = true; | |
| 153 | |
| 154 /** Whether the Tab has already been added to the TabModel. */ | |
| 155 private boolean mNeedsToBeAddedToTabModel; | |
| 156 | |
| 157 @Override | 31 @Override |
| 158 protected boolean isStartedUpCorrectly(Intent intent) { | 32 protected boolean isStartedUpCorrectly(Intent intent) { |
| 159 boolean isDocumentMode = FeatureUtilities.isDocumentMode(this); | |
| 160 int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); | 33 int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); |
| 161 | 34 |
| 162 if (!isDocumentMode) { | 35 // Fire a MAIN Intent to send the user back through ChromeLauncherActivi
ty. |
| 163 // Fire a MAIN Intent to send the user back through ChromeLauncherAc
tivity. | 36 Log.e(TAG, "User shouldn't be here. Sending back to ChromeLauncherActiv
ity."); |
| 164 Log.e(TAG, "User is not in document mode. Sending back to ChromeLau
ncherActivity."); | |
| 165 | 37 |
| 166 // Try to bring this tab forward after migration. | 38 // Try to bring this tab forward after migration. |
| 167 Intent tabbedIntent = null; | 39 Intent tabbedIntent = null; |
| 168 if (tabId != Tab.INVALID_TAB_ID) tabbedIntent = Tab.createBringTabTo
FrontIntent(tabId); | 40 if (tabId != Tab.INVALID_TAB_ID) tabbedIntent = Tab.createBringTabToFron
tIntent(tabId); |
| 169 | 41 |
| 170 if (tabbedIntent == null) { | 42 if (tabbedIntent == null) { |
| 171 tabbedIntent = new Intent(Intent.ACTION_MAIN); | 43 tabbedIntent = new Intent(Intent.ACTION_MAIN); |
| 172 tabbedIntent.setPackage(getPackageName()); | 44 tabbedIntent.setPackage(getPackageName()); |
| 173 } | |
| 174 | |
| 175 // Launch the other Activity in its own task so it stays when this o
ne finishes. | |
| 176 tabbedIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | |
| 177 tabbedIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); | |
| 178 | |
| 179 startActivity(tabbedIntent); | |
| 180 overridePendingTransition(android.R.anim.fade_in, android.R.anim.fad
e_out); | |
| 181 } | 45 } |
| 182 | 46 |
| 183 boolean isStartedUpCorrectly = tabId != Tab.INVALID_TAB_ID && isDocument
Mode; | 47 // Launch the other Activity in its own task so it stays when this one f
inishes. |
| 184 if (!isStartedUpCorrectly) { | 48 tabbedIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| 185 Log.e(TAG, "Discarding Intent: Tab = " + tabId + ", Document mode =
" + isDocumentMode); | 49 tabbedIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); |
| 186 } | 50 |
| 187 return isStartedUpCorrectly; | 51 startActivity(tabbedIntent); |
| 52 overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_ou
t); |
| 53 |
| 54 Log.e(TAG, "Discarding Intent: Tab = " + tabId); |
| 55 return false; |
| 188 } | 56 } |
| 189 | 57 |
| 190 @Override | 58 @Override |
| 191 public void preInflationStartup() { | 59 protected boolean handleBackPressed() { |
| 192 // Decide whether to record startup UMA histograms. This is done early
in the main | |
| 193 // Activity.onCreate() to avoid recording navigation delays when they re
quire user input to | |
| 194 // proceed. See more details in ChromeTabbedActivity.preInflationStartup
(). | |
| 195 if (!LibraryLoader.isInitialized()) { | |
| 196 UmaUtils.setRunningApplicationStart(true); | |
| 197 } | |
| 198 | |
| 199 DocumentTabModelSelector.setPrioritizedTabId( | |
| 200 ActivityDelegate.getTabIdFromIntent(getIntent())); | |
| 201 | |
| 202 mTabModel = ChromeApplication.getDocumentTabModelSelector().getModel(isI
ncognito()); | |
| 203 mTabModel.startTabStateLoad(); | |
| 204 updateLastTabId(); | |
| 205 | |
| 206 SingleTabModelSelector selector = new SingleTabModelSelector(this, isInc
ognito(), false) { | |
| 207 @Override | |
| 208 public Tab openNewTab(LoadUrlParams loadUrlParams, TabLaunchType typ
e, Tab parent, | |
| 209 boolean incognito) { | |
| 210 // Triggered via open in new tab context menu on NTP. | |
| 211 return ChromeApplication.getDocumentTabModelSelector().openNewTa
b( | |
| 212 loadUrlParams, type, parent, incognito); | |
| 213 } | |
| 214 }; | |
| 215 setTabModelSelector(selector); | |
| 216 setTabCreators(ChromeApplication.getDocumentTabModelSelector().getTabCre
ator(false), | |
| 217 ChromeApplication.getDocumentTabModelSelector().getTabCreator(tr
ue)); | |
| 218 | |
| 219 super.preInflationStartup(); | |
| 220 | |
| 221 supportRequestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY); | |
| 222 } | |
| 223 | |
| 224 @Override | |
| 225 protected int getControlContainerLayoutId() { | |
| 226 return R.layout.control_container; | |
| 227 } | |
| 228 | |
| 229 @Override | |
| 230 public void postInflationStartup() { | |
| 231 super.postInflationStartup(); | |
| 232 | |
| 233 final int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); | |
| 234 mTabInitializationObserver = new InitializationObserver(mTabModel) { | |
| 235 @Override | |
| 236 public boolean isSatisfied(int currentState) { | |
| 237 return currentState >= DocumentTabModelImpl.STATE_LOAD_TAB_S
TATE_BG_END | |
| 238 || mTabModel.isTabStateReady(tabId); | |
| 239 } | |
| 240 | |
| 241 @Override | |
| 242 public boolean isCanceled() { | |
| 243 return isDestroyed() || isFinishing(); | |
| 244 } | |
| 245 | |
| 246 @Override | |
| 247 public void runImmediately() { | |
| 248 initializeUI(); | |
| 249 } | |
| 250 }; | |
| 251 } | |
| 252 | |
| 253 @Override | |
| 254 public void prepareMenu(Menu menu) { | |
| 255 if (isNewTabPage() && !isIncognito()) { | |
| 256 menu.findItem(R.id.new_tab_menu_id).setVisible(false); | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 @Override | |
| 261 public void finishNativeInitialization() { | |
| 262 ChromePreferenceManager preferenceManager = | |
| 263 ChromePreferenceManager.getInstance(this); | |
| 264 if (isNewTabPage() && FirstRunStatus.getFirstRunFlowComplete(this)) { | |
| 265 // Only show promos on second NTP. | |
| 266 if (preferenceManager.getPromosSkippedOnFirstStart()) { | |
| 267 // Data reduction promo should be temporarily suppressed if the
sign in promo is | |
| 268 // shown to avoid nagging users too much. | |
| 269 if (!SigninPromoUtil.launchSigninPromoIfNeeded(this)) { | |
| 270 DataReductionPromoScreen.launchDataReductionPromo(this); | |
| 271 } | |
| 272 } else { | |
| 273 preferenceManager.setPromosSkippedOnFirstStart(true); | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 FirstRunSignInProcessor.start(this); | |
| 278 | |
| 279 getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS, | |
| 280 Window.PROGRESS_VISIBILITY_OFF); | |
| 281 | |
| 282 // Initialize the native-side TabModels so that the Profile is available
when necessary. | |
| 283 ChromeApplication.getDocumentTabModelSelector().onNativeLibraryReady(); | |
| 284 mTabInitializationObserver.runWhenReady(); | |
| 285 | |
| 286 if (mNeedsToBeAddedToTabModel) { | |
| 287 mNeedsToBeAddedToTabModel = false; | |
| 288 mTabModel.addTab(getIntent(), mTab); | |
| 289 getTabModelSelector().setTab(mTab); | |
| 290 } | |
| 291 | |
| 292 super.finishNativeInitialization(); | |
| 293 } | |
| 294 | |
| 295 /** | |
| 296 * @return The ID of the Tab. | |
| 297 */ | |
| 298 protected final int determineTabId() { | |
| 299 int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); | |
| 300 if (tabId == Tab.INVALID_TAB_ID && mTab != null) tabId = mTab.getId(); | |
| 301 return tabId; | |
| 302 } | |
| 303 | |
| 304 /** | |
| 305 * Determine the last known URL that this Document was displaying when it wa
s stopped. | |
| 306 * @return URL that the user was last known to have seen, or null if one cou
ldn't be found. | |
| 307 */ | |
| 308 private String determineLastKnownUrl() { | |
| 309 int tabId = determineTabId(); | |
| 310 String url = mTabModel.getCurrentUrlForDocument(tabId); | |
| 311 if (TextUtils.isEmpty(url)) url = determineInitialUrl(tabId); | |
| 312 return url; | |
| 313 } | |
| 314 | |
| 315 /** | |
| 316 * Determine the URL that this Document was initially opened for. It can be
stashed in multiple | |
| 317 * locations because IncognitoDocumentActivities are disallowed from passing
URLs in the Intent. | |
| 318 * @param tabId ID of the Tab owned by this Activity. | |
| 319 * @return Initial URL, or null if it couldn't be determined. | |
| 320 */ | |
| 321 protected String determineInitialUrl(int tabId) { | |
| 322 String initialUrl = null; | |
| 323 | |
| 324 // Check if the TabModel has it. | |
| 325 if (mTabModel != null) { | |
| 326 initialUrl = mTabModel.getInitialUrlForDocument(tabId); | |
| 327 } | |
| 328 | |
| 329 // Check if the URL was passed through the Intent. | |
| 330 if (TextUtils.isEmpty(initialUrl) && getIntent() != null) { | |
| 331 initialUrl = IntentHandler.getUrlFromIntent(getIntent()); | |
| 332 } | |
| 333 | |
| 334 // Check the Tab's history. | |
| 335 if (TextUtils.isEmpty(initialUrl) && mTab != null | |
| 336 && mTab.getWebContents() != null) { | |
| 337 NavigationEntry entry = | |
| 338 mTab.getWebContents().getNavigationController().getEntryAtIn
dex(0); | |
| 339 if (entry != null) initialUrl = entry.getOriginalUrl(); | |
| 340 } | |
| 341 | |
| 342 return initialUrl; | |
| 343 } | |
| 344 | |
| 345 @Override | |
| 346 public CharSequence onCreateDescription() { | |
| 347 return mTab != null ? mTab.getTitle() : ""; | |
| 348 } | |
| 349 | |
| 350 @Override | |
| 351 public final Tab getActivityTab() { | |
| 352 return mTab; | |
| 353 } | |
| 354 | |
| 355 @Override | |
| 356 public void onStartWithNative() { | |
| 357 super.onStartWithNative(); | |
| 358 handleDocumentUma(); | |
| 359 } | |
| 360 | |
| 361 @Override | |
| 362 public void onStopWithNative() { | |
| 363 super.onStopWithNative(); | |
| 364 StartupMetrics.getInstance().recordHistogram(true); | |
| 365 } | |
| 366 | |
| 367 @Override | |
| 368 protected void onDestroyInternal() { | |
| 369 super.onDestroyInternal(); | |
| 370 | |
| 371 if (mTaskDescriptionHelper != null) mTaskDescriptionHelper.destroy(); | |
| 372 } | |
| 373 | |
| 374 private void handleDocumentUma() { | |
| 375 if (mRecordedStartupUma) { | |
| 376 DocumentUma.recordStartedBy( | |
| 377 DocumentMetricIds.STARTED_BY_ACTIVITY_BROUGHT_TO_FOREGROUND)
; | |
| 378 } else { | |
| 379 mRecordedStartupUma = true; | |
| 380 | |
| 381 if (getSavedInstanceState() == null) { | |
| 382 DocumentUma.recordStartedBy(getPackageName(), getIntent()); | |
| 383 } else { | |
| 384 DocumentUma.recordStartedBy(DocumentMetricIds.STARTED_BY_ACTIVIT
Y_RESTARTED); | |
| 385 } | |
| 386 } | |
| 387 DocumentUma.recordInDocumentMode(true); | |
| 388 } | |
| 389 | |
| 390 @Override | |
| 391 public void onPause() { | |
| 392 // If finishing, release all the active media players as we don't know w
hen onStop() | |
| 393 // will get called. | |
| 394 super.onPause(); | |
| 395 if (isFinishing() && mTab != null && mTab.getWebContents() != null) { | |
| 396 mTab.getWebContents().suspendAllMediaPlayers(); | |
| 397 mTab.getWebContents().setAudioMuted(true); | |
| 398 } | |
| 399 } | |
| 400 | |
| 401 @Override | |
| 402 public void onWindowFocusChanged(boolean hasWindowFocus) { | |
| 403 super.onWindowFocusChanged(hasWindowFocus); | |
| 404 if (hasWindowFocus) updateLastTabId(); | |
| 405 } | |
| 406 | |
| 407 @Override | |
| 408 public void onResume() { | |
| 409 super.onResume(); | |
| 410 | |
| 411 if (getIntent() != null) { | |
| 412 DocumentUtils.finishOtherTasksWithTabID( | |
| 413 ActivityDelegate.getTabIdFromIntent(getIntent()), getTaskId(
)); | |
| 414 } | |
| 415 } | |
| 416 | |
| 417 @Override | |
| 418 public void onResumeWithNative() { | |
| 419 super.onResumeWithNative(); | |
| 420 | |
| 421 if (mTab != null) { | |
| 422 AsyncTabParams asyncParams = AsyncTabParamsManager.remove( | |
| 423 ActivityDelegate.getTabIdFromIntent(getIntent())); | |
| 424 if (asyncParams != null && asyncParams.getLoadUrlParams().getUrl() !
= null) { | |
| 425 loadLastKnownUrl(asyncParams); | |
| 426 } | |
| 427 } | |
| 428 StartupMetrics.getInstance().recordHistogram(false); | |
| 429 } | |
| 430 | |
| 431 @Override | |
| 432 public SingleTabModelSelector getTabModelSelector() { | |
| 433 return (SingleTabModelSelector) super.getTabModelSelector(); | |
| 434 } | |
| 435 | |
| 436 private void loadLastKnownUrl(AsyncTabParams asyncParams) { | |
| 437 Intent intent = getIntent(); | |
| 438 if (asyncParams != null && asyncParams.getOriginalIntent() != null) { | |
| 439 intent = asyncParams.getOriginalIntent(); | |
| 440 } | |
| 441 | |
| 442 boolean isIntentChromeOrFirstParty = IntentHandler.isIntentChromeOrFirst
Party(intent, | |
| 443 getApplicationContext()); | |
| 444 int transitionType = intent == null ? PageTransition.LINK | |
| 445 : IntentUtils.safeGetIntExtra(intent, IntentHandler.EXTRA_PAGE_T
RANSITION_TYPE, | |
| 446 PageTransition.LINK); | |
| 447 if ((transitionType != PageTransition.TYPED) | |
| 448 && (transitionType != PageTransition.LINK) | |
| 449 && !isIntentChromeOrFirstParty) { | |
| 450 // Only 1st party authenticated applications can set the transition
type to be | |
| 451 // anything other than TYPED for security reasons. | |
| 452 transitionType = PageTransition.LINK; | |
| 453 } | |
| 454 if (transitionType == PageTransition.LINK) { | |
| 455 transitionType |= PageTransition.FROM_API; | |
| 456 } | |
| 457 | |
| 458 String url = (asyncParams != null && asyncParams.getLoadUrlParams().getU
rl() != null) | |
| 459 ? asyncParams.getLoadUrlParams().getUrl() : determineLastKnownUr
l(); | |
| 460 | |
| 461 LoadUrlParams loadUrlParams = | |
| 462 asyncParams == null ? new LoadUrlParams(url) : asyncParams.getLo
adUrlParams(); | |
| 463 loadUrlParams.setTransitionType(transitionType); | |
| 464 if (getIntent() != null) { | |
| 465 loadUrlParams.setIntentReceivedTimestamp(getOnCreateTimestampUptimeM
s()); | |
| 466 } | |
| 467 | |
| 468 if (intent != null) { | |
| 469 IntentHandler.addReferrerAndHeaders(loadUrlParams, intent, this); | |
| 470 } | |
| 471 | |
| 472 if (asyncParams != null && asyncParams.getOriginalIntent() != null) { | |
| 473 mTab.getTabRedirectHandler().updateIntent(asyncParams.getOriginalInt
ent()); | |
| 474 } else { | |
| 475 if (getIntent() != null) { | |
| 476 try { | |
| 477 intent = IntentUtils.safeGetParcelableExtra(getIntent(), | |
| 478 IntentHandler.EXTRA_ORIGINAL_INTENT); | |
| 479 } catch (Throwable t) { | |
| 480 // Ignore exception. | |
| 481 } | |
| 482 } | |
| 483 mTab.getTabRedirectHandler().updateIntent(intent); | |
| 484 } | |
| 485 | |
| 486 mTab.loadUrl(loadUrlParams); | |
| 487 if (asyncParams != null && asyncParams.getRequestId() != null | |
| 488 && asyncParams.getRequestId() > 0) { | |
| 489 ServiceTabLauncher.onWebContentsForRequestAvailable( | |
| 490 asyncParams.getRequestId(), mTab.getWebContents()); | |
| 491 } | |
| 492 } | |
| 493 | |
| 494 @Override | |
| 495 public void terminateIncognitoSession() { | |
| 496 ChromeApplication.getDocumentTabModelSelector().getModel(true).closeAllT
abs(); | |
| 497 } | |
| 498 | |
| 499 private void initializeUI() { | |
| 500 int defaultThemeColor = isIncognito() | |
| 501 ? ApiCompatibilityUtils.getColor(getResources(), R.color.incogni
to_primary_color) | |
| 502 : ApiCompatibilityUtils.getColor(getResources(), R.color.default
_primary_color); | |
| 503 mTaskDescriptionHelper = new ActivityTabTaskDescriptionHelper(this, defa
ultThemeColor); | |
| 504 | |
| 505 AsyncTabParams params = AsyncTabParamsManager.remove( | |
| 506 ActivityDelegate.getTabIdFromIntent(getIntent())); | |
| 507 | |
| 508 AsyncTabCreationParams asyncParams = params instanceof AsyncTabCreationP
arams | |
| 509 ? (AsyncTabCreationParams) params : null; | |
| 510 boolean isAffiliated = asyncParams != null ? asyncParams.isAffiliated()
: false; | |
| 511 boolean isCreatedWithWebContents = asyncParams != null | |
| 512 && asyncParams.getWebContents() != null; | |
| 513 | |
| 514 // URL modification must happen before the Tab is created. | |
| 515 if (!isIncognito() && asyncParams != null) { | |
| 516 LoadUrlParams loadUrlParams = asyncParams.getLoadUrlParams(); | |
| 517 if (loadUrlParams != null && loadUrlParams.getUrl() != null) { | |
| 518 loadUrlParams.setUrl(DataReductionProxySettings.getInstance() | |
| 519 .maybeRewriteWebliteUrl(loadUrlParams.getUrl())); | |
| 520 } | |
| 521 } | |
| 522 | |
| 523 if (params != null && params.getTabToReparent() != null) { | |
| 524 mTab = params.getTabToReparent(); | |
| 525 mTab.attachAndFinishReparenting(this, new DocumentTabDelegateFactory
(), | |
| 526 (TabReparentingParams) params); | |
| 527 } else { | |
| 528 mTab = createActivityTab(asyncParams); | |
| 529 } | |
| 530 | |
| 531 if (asyncParams != null && asyncParams.getWebContents() != null) { | |
| 532 Intent parentIntent = IntentUtils.safeGetParcelableExtra(getIntent()
, | |
| 533 IntentHandler.EXTRA_PARENT_INTENT); | |
| 534 mTab.setParentIntent(parentIntent); | |
| 535 } | |
| 536 | |
| 537 if (mTabModel.isNativeInitialized()) { | |
| 538 mTabModel.addTab(getIntent(), mTab); | |
| 539 } else { | |
| 540 mNeedsToBeAddedToTabModel = true; | |
| 541 } | |
| 542 | |
| 543 getTabModelSelector().setTab(mTab); | |
| 544 | |
| 545 if (mTab.didFailToRestore() | |
| 546 || (asyncParams != null && asyncParams.getLoadUrlParams().getUrl
() != null)) { | |
| 547 if (!isCreatedWithWebContents) { | |
| 548 // Don't load tabs in the background on low end devices. We will
call | |
| 549 // loadLastKnownUrl() in onResumeWithNative() next time activity
is resumed. | |
| 550 if (SysUtils.isLowEndDevice() && isAffiliated) { | |
| 551 // onResumeWithNative() wants asyncParams's URL to be non-nu
ll. | |
| 552 LoadUrlParams loadUrlParams = asyncParams.getLoadUrlParams()
; | |
| 553 if (loadUrlParams.getUrl() == null) { | |
| 554 loadUrlParams.setUrl(determineLastKnownUrl()); | |
| 555 } | |
| 556 | |
| 557 AsyncTabParamsManager.add( | |
| 558 ActivityDelegate.getTabIdFromIntent(getIntent()), as
yncParams); | |
| 559 | |
| 560 // Use the URL as the document title until tab is loaded. | |
| 561 mTaskDescriptionHelper.updateTaskDescription(loadUrlParams.g
etUrl(), null); | |
| 562 } else { | |
| 563 loadLastKnownUrl(asyncParams); | |
| 564 } | |
| 565 } | |
| 566 mTab.setShouldPreserve(IntentUtils.safeGetBooleanExtra(getIntent(), | |
| 567 IntentHandler.EXTRA_PRESERVE_TASK, false)); | |
| 568 } | |
| 569 | |
| 570 ToolbarControlContainer controlContainer = | |
| 571 (ToolbarControlContainer) findViewById(R.id.control_container); | |
| 572 LayoutManagerDocument layoutDriver = null; | |
| 573 OverviewModeBehavior overviewModeBehavior = null; | |
| 574 OnClickListener tabSwitcherClickHandler = null; | |
| 575 if (FeatureUtilities.isTabSwitchingEnabledInDocumentMode(getApplicationC
ontext())) { | |
| 576 LayoutManagerDocumentTabSwitcher layoutDriverTabSwitcher = | |
| 577 new LayoutManagerDocumentTabSwitcher(getCompositorViewHolder
()); | |
| 578 layoutDriverTabSwitcher.addOverviewModeObserver(new OverviewModeObse
rver() { | |
| 579 @Override | |
| 580 public void onOverviewModeStartedShowing(boolean showToolbar) { | |
| 581 if (mFindToolbarManager != null) mFindToolbarManager.hideToo
lbar(); | |
| 582 if (getAssistStatusHandler() != null) { | |
| 583 getAssistStatusHandler().updateAssistState(); | |
| 584 } | |
| 585 ApiCompatibilityUtils.setStatusBarColor(getWindow(), Color.B
LACK); | |
| 586 StartupMetrics.getInstance().recordOpenedTabSwitcher(); | |
| 587 } | |
| 588 | |
| 589 @Override | |
| 590 public void onOverviewModeFinishedShowing() {} | |
| 591 | |
| 592 @Override | |
| 593 public void onOverviewModeStartedHiding( | |
| 594 boolean showToolbar, boolean delayAnimation) {} | |
| 595 | |
| 596 @Override | |
| 597 public void onOverviewModeFinishedHiding() { | |
| 598 if (getAssistStatusHandler() != null) { | |
| 599 getAssistStatusHandler().updateAssistState(); | |
| 600 } | |
| 601 if (getActivityTab() != null) { | |
| 602 LayoutManagerDocumentTabSwitcher manager = | |
| 603 (LayoutManagerDocumentTabSwitcher) getCompositor
ViewHolder() | |
| 604 .getLayoutManager(); | |
| 605 boolean isInOverviewMode = manager != null && manager.ov
erviewVisible(); | |
| 606 setStatusBarColor(getActivityTab(), | |
| 607 isInOverviewMode ? Color.BLACK : getActivityTab(
).getThemeColor()); | |
| 608 } | |
| 609 } | |
| 610 }); | |
| 611 layoutDriver = layoutDriverTabSwitcher; | |
| 612 overviewModeBehavior = layoutDriverTabSwitcher; | |
| 613 | |
| 614 tabSwitcherClickHandler = new OnClickListener() { | |
| 615 @Override | |
| 616 public void onClick(View v) { | |
| 617 if (getFullscreenManager() != null | |
| 618 && getFullscreenManager().getPersistentFullscreenMod
e()) { | |
| 619 return; | |
| 620 } | |
| 621 LayoutManagerDocumentTabSwitcher manager = | |
| 622 (LayoutManagerDocumentTabSwitcher) getCompositorView
Holder() | |
| 623 .getLayoutManager(); | |
| 624 manager.toggleOverview(); | |
| 625 } | |
| 626 }; | |
| 627 } else { | |
| 628 layoutDriver = new LayoutManagerDocument(getCompositorViewHolder()); | |
| 629 } | |
| 630 initializeCompositorContent(layoutDriver, findViewById(R.id.url_bar), | |
| 631 (ViewGroup) findViewById(android.R.id.content), controlContainer
); | |
| 632 | |
| 633 mFindToolbarManager = new FindToolbarManager(this, | |
| 634 getToolbarManager().getActionModeController().getActionModeCallb
ack()); | |
| 635 | |
| 636 if (getContextualSearchManager() != null) { | |
| 637 getContextualSearchManager().setFindToolbarManager(mFindToolbarManag
er); | |
| 638 } | |
| 639 | |
| 640 getToolbarManager().initializeWithNative(getTabModelSelector(), getFulls
creenManager(), | |
| 641 mFindToolbarManager, overviewModeBehavior, layoutDriver, tabSwit
cherClickHandler, | |
| 642 null, null, null); | |
| 643 | |
| 644 mTab.setFullscreenManager(getFullscreenManager()); | |
| 645 | |
| 646 mTab.addObserver(new DocumentTabObserver()); | |
| 647 | |
| 648 removeWindowBackground(); | |
| 649 } | |
| 650 | |
| 651 private void updateLastTabId() { | |
| 652 ChromeApplication.getDocumentTabModelSelector().selectModel(isIncognito(
)); | |
| 653 int tabId = mTab == null | |
| 654 ? ActivityDelegate.getTabIdFromIntent(getIntent()) : mTab.getId
(); | |
| 655 mTabModel.setLastShownId(tabId); | |
| 656 } | |
| 657 | |
| 658 @Override | |
| 659 public boolean handleBackPressed() { | |
| 660 if (mTab == null) return false; | |
| 661 | |
| 662 if (exitFullscreenIfShowing()) return true; | |
| 663 | |
| 664 if (mTab.canGoBack()) { | |
| 665 mTab.goBack(); | |
| 666 } else if (!mTab.shouldPreserve()) { | |
| 667 finishAndRemoveTask(); | |
| 668 } else { | |
| 669 moveTaskToBack(true); | |
| 670 } | |
| 671 return true; | |
| 672 } | |
| 673 | |
| 674 | |
| 675 @Override | |
| 676 public TabDelegate getTabCreator(boolean incognito) { | |
| 677 return (TabDelegate) super.getTabCreator(incognito); | |
| 678 } | |
| 679 | |
| 680 private Tab createActivityTab(AsyncTabCreationParams asyncParams) { | |
| 681 boolean isAffiliated = asyncParams != null ? asyncParams.isAffiliated()
: false; | |
| 682 boolean isCreatedWithWebContents = asyncParams != null | |
| 683 && asyncParams.getWebContents() != null; | |
| 684 int tabId = determineTabId(); | |
| 685 int parentTabId = getIntent().getIntExtra( | |
| 686 IntentHandler.EXTRA_PARENT_TAB_ID, Tab.INVALID_TAB_ID); | |
| 687 TabState tabState = mTabModel.getTabStateForDocument(tabId); | |
| 688 boolean hasTabState = tabState != null; | |
| 689 String url = determineLastKnownUrl(); | |
| 690 | |
| 691 Tab tab = new Tab(tabId, parentTabId, isIncognito(), this, getWindowAndr
oid(), | |
| 692 getTabLaunchType(url, hasTabState, isAffiliated), | |
| 693 getTabCreationState(hasTabState, isAffiliated), tabState); | |
| 694 | |
| 695 // Initialize tab and web contents. | |
| 696 WebContents webContents = isCreatedWithWebContents ? asyncParams.getWebC
ontents() : null; | |
| 697 tab.initialize(webContents, getTabContentManager(), | |
| 698 new DocumentTabDelegateFactory(), isAffiliated, hasTabState); | |
| 699 tab.getView().requestFocus(); | |
| 700 if (isCreatedWithWebContents) webContents.resumeLoadingCreatedWebContent
s(); | |
| 701 | |
| 702 return tab; | |
| 703 } | |
| 704 | |
| 705 /** | |
| 706 * This cannot return {@link TabCreationState#FROZEN_ON_RESTORE_FAILED} sinc
e the Tab has | |
| 707 * to be created first to even attempt restore. | |
| 708 */ | |
| 709 private TabCreationState getTabCreationState(boolean hasTabState, boolean is
Affiliated) { | |
| 710 if (hasTabState) return TabCreationState.FROZEN_ON_RESTORE; | |
| 711 if (isAffiliated) { | |
| 712 if (SysUtils.isLowEndDevice()) return TabCreationState.FROZEN_FOR_LA
ZY_LOAD; | |
| 713 return TabCreationState.LIVE_IN_BACKGROUND; | |
| 714 } | |
| 715 return TabCreationState.LIVE_IN_FOREGROUND; | |
| 716 } | |
| 717 | |
| 718 private TabLaunchType getTabLaunchType( | |
| 719 String url, boolean hasTabState, boolean isAffiliated) { | |
| 720 if (hasTabState) return TabLaunchType.FROM_RESTORE; | |
| 721 if (isAffiliated) return TabLaunchType.FROM_LONGPRESS_BACKGROUND; | |
| 722 if (!TextUtils.isEmpty(url) && url.equals(UrlConstants.NTP_URL)) { | |
| 723 return TabLaunchType.FROM_CHROME_UI; | |
| 724 } | |
| 725 return TabLaunchType.FROM_EXTERNAL_APP; | |
| 726 } | |
| 727 | |
| 728 @Override | |
| 729 public void createContextualSearchTab(String searchUrl) { | |
| 730 AsyncTabCreationParams asyncParams = | |
| 731 new AsyncTabCreationParams(new LoadUrlParams(searchUrl, PageTran
sition.LINK)); | |
| 732 asyncParams.setDocumentStartedBy(DocumentMetricIds.STARTED_BY_CONTEXTUAL
_SEARCH); | |
| 733 getTabCreator(false).createNewTab( | |
| 734 asyncParams, TabLaunchType.FROM_CHROME_UI, getActivityTab().getI
d()); | |
| 735 } | |
| 736 | |
| 737 @Override | |
| 738 public boolean onMenuOrKeyboardAction(int id, boolean fromMenu) { | |
| 739 if (id == R.id.new_tab_menu_id) { | |
| 740 mHandler.postDelayed(new Runnable() { | |
| 741 @Override | |
| 742 public void run() { | |
| 743 launchNtp(false); | |
| 744 } | |
| 745 }, MENU_EXIT_ANIMATION_WAIT_MS); | |
| 746 RecordUserAction.record("MobileMenuNewTab"); | |
| 747 RecordUserAction.record("MobileNewTabOpened"); | |
| 748 } else if (id == R.id.new_incognito_tab_menu_id) { | |
| 749 // This action must be recorded before opening the incognito tab sin
ce UMA actions | |
| 750 // are dropped when an incognito tab is open. | |
| 751 RecordUserAction.record("MobileMenuNewIncognitoTab"); | |
| 752 RecordUserAction.record("MobileNewTabOpened"); | |
| 753 mHandler.postDelayed(new Runnable() { | |
| 754 @Override | |
| 755 public void run() { | |
| 756 launchNtp(true); | |
| 757 } | |
| 758 }, MENU_EXIT_ANIMATION_WAIT_MS); | |
| 759 } else if (id == R.id.all_bookmarks_menu_id) { | |
| 760 StartupMetrics.getInstance().recordOpenedBookmarks(); | |
| 761 BookmarkUtils.showBookmarkManager(this); | |
| 762 RecordUserAction.record("MobileMenuAllBookmarks"); | |
| 763 } else if (id == R.id.recent_tabs_menu_id) { | |
| 764 NewTabPage.launchRecentTabsDialog(this, mTab); | |
| 765 RecordUserAction.record("MobileMenuOpenTabs"); | |
| 766 } else if (id == R.id.find_in_page_id) { | |
| 767 mFindToolbarManager.showToolbar(); | |
| 768 if (getContextualSearchManager() != null) { | |
| 769 getContextualSearchManager().hideContextualSearch(StateChangeRea
son.UNKNOWN); | |
| 770 } | |
| 771 if (fromMenu) { | |
| 772 RecordUserAction.record("MobileMenuFindInPage"); | |
| 773 } else { | |
| 774 RecordUserAction.record("MobileShortcutFindInPage"); | |
| 775 } | |
| 776 } else if (id == R.id.focus_url_bar) { | |
| 777 if (getToolbarManager().isInitialized()) getToolbarManager().setUrlB
arFocus(true); | |
| 778 } else { | |
| 779 return super.onMenuOrKeyboardAction(id, fromMenu); | |
| 780 } | |
| 781 return true; | |
| 782 } | |
| 783 | |
| 784 @Override | |
| 785 public boolean dispatchKeyEvent(KeyEvent event) { | |
| 786 Boolean result = KeyboardShortcuts.dispatchKeyEvent(event, this, | |
| 787 getToolbarManager().isInitialized()); | |
| 788 return result != null ? result : super.dispatchKeyEvent(event); | |
| 789 } | |
| 790 | |
| 791 @Override | |
| 792 public boolean onKeyDown(int keyCode, KeyEvent event) { | |
| 793 if (!getToolbarManager().isInitialized()) { | |
| 794 return super.onKeyDown(keyCode, event); | |
| 795 } | |
| 796 return KeyboardShortcuts.onKeyDown(event, this, true, false) | |
| 797 || super.onKeyDown(keyCode, event); | |
| 798 } | |
| 799 | |
| 800 @Override | |
| 801 public boolean shouldShowAppMenu() { | |
| 802 if (mTab == null || !getToolbarManager().isInitialized()) { | |
| 803 return false; | |
| 804 } | |
| 805 | |
| 806 return super.shouldShowAppMenu(); | |
| 807 } | |
| 808 | |
| 809 @Override | |
| 810 protected void showAppMenuForKeyboardEvent() { | |
| 811 if (!getToolbarManager().isInitialized()) return; | |
| 812 super.showAppMenuForKeyboardEvent(); | |
| 813 } | |
| 814 | |
| 815 /** | |
| 816 * @return Whether the {@link DocumentTab} this activity uses is incognito. | |
| 817 */ | |
| 818 protected boolean isIncognito() { | |
| 819 return false; | 60 return false; |
| 820 } | 61 } |
| 821 | 62 |
| 822 /** | 63 /** |
| 823 * @return Whether this activity contains a tab that is showing the new tab
page. | |
| 824 */ | |
| 825 boolean isNewTabPage() { | |
| 826 String url; | |
| 827 if (mTab == null) { | |
| 828 // If the Tab hasn't been created yet, then we're really early in in
itialization. | |
| 829 // Use a combination of the original URL from the Intent and whether
or not the Tab is | |
| 830 // retargetable to know whether or not the user navigated away from
the NTP. | |
| 831 // If the Entry doesn't exist, then the DocumentActivity never got a
chance to add | |
| 832 // itself to the TabList and is likely to be retargetable. | |
| 833 int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); | |
| 834 url = IntentHandler.getUrlFromIntent(getIntent()); | |
| 835 if (mTabModel.hasEntryForTabId(tabId) && !mTabModel.isRetargetable(t
abId)) return false; | |
| 836 } else { | |
| 837 url = mTab.getUrl(); | |
| 838 } | |
| 839 return NewTabPage.isNTPUrl(url); | |
| 840 } | |
| 841 | |
| 842 /** | |
| 843 * Determines whether the given class can be classified as a DocumentActivit
y (this includes | 64 * Determines whether the given class can be classified as a DocumentActivit
y (this includes |
| 844 * both regular document activity and incognito document activity). | 65 * both regular document activity and incognito document activity). |
| 845 * @param className The class name to inspect. | 66 * @param className The class name to inspect. |
| 846 * @return Whether the className is that of a document activity. | 67 * @return Whether the className is that of a document activity. |
| 847 */ | 68 */ |
| 848 public static boolean isDocumentActivity(String className) { | 69 public static boolean isDocumentActivity(String className) { |
| 849 return TextUtils.equals(className, IncognitoDocumentActivity.class.getNa
me()) | 70 return TextUtils.equals(className, IncognitoDocumentActivity.class.getNa
me()) |
| 850 || TextUtils.equals(className, DocumentActivity.class.getName()) | 71 || TextUtils.equals(className, DocumentActivity.class.getName()) |
| 851 || TextUtils.equals(className, LEGACY_CLASS_NAME) | 72 || TextUtils.equals(className, LEGACY_CLASS_NAME) |
| 852 || TextUtils.equals(className, LEGACY_INCOGNITO_CLASS_NAME); | 73 || TextUtils.equals(className, LEGACY_INCOGNITO_CLASS_NAME); |
| 853 } | 74 } |
| 854 | |
| 855 /** | |
| 856 * Launch a new DocumentActivity showing the new tab page. | |
| 857 * @param incognito Whether the new NTP should be in incognito mode. | |
| 858 */ | |
| 859 private void launchNtp(boolean incognito) { | |
| 860 if (incognito && !PrefServiceBridge.getInstance().isIncognitoModeEnabled
()) return; | |
| 861 getTabCreator(incognito).launchNTP(); | |
| 862 } | |
| 863 } | 75 } |
| OLD | NEW |