OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser; |
| 6 |
| 7 import android.app.ActivityManager; |
| 8 import android.app.SearchManager; |
| 9 import android.content.Context; |
| 10 import android.content.Intent; |
| 11 import android.content.res.Configuration; |
| 12 import android.graphics.Rect; |
| 13 import android.os.Build; |
| 14 import android.os.Bundle; |
| 15 import android.os.SystemClock; |
| 16 import android.text.TextUtils; |
| 17 import android.util.Log; |
| 18 import android.view.KeyEvent; |
| 19 import android.view.View; |
| 20 import android.view.View.OnClickListener; |
| 21 import android.view.ViewGroup; |
| 22 import android.view.Window; |
| 23 import android.view.WindowManager; |
| 24 import android.widget.FrameLayout; |
| 25 import android.widget.Toast; |
| 26 |
| 27 import com.google.android.apps.chrome.R; |
| 28 |
| 29 import org.chromium.base.CommandLine; |
| 30 import org.chromium.base.MemoryPressureListener; |
| 31 import org.chromium.base.TraceEvent; |
| 32 import org.chromium.base.VisibleForTesting; |
| 33 import org.chromium.base.library_loader.LibraryLoader; |
| 34 import org.chromium.base.metrics.RecordHistogram; |
| 35 import org.chromium.base.metrics.RecordUserAction; |
| 36 import org.chromium.chrome.browser.ContextualMenuBar.ActionBarDelegate; |
| 37 import org.chromium.chrome.browser.IntentHandler.IntentHandlerDelegate; |
| 38 import org.chromium.chrome.browser.IntentHandler.TabOpenType; |
| 39 import org.chromium.chrome.browser.appmenu.AppMenuHandler; |
| 40 import org.chromium.chrome.browser.appmenu.AppMenuObserver; |
| 41 import org.chromium.chrome.browser.appmenu.ChromeAppMenuPropertiesDelegate; |
| 42 import org.chromium.chrome.browser.compositor.CompositorViewHolder; |
| 43 import org.chromium.chrome.browser.compositor.layouts.Layout; |
| 44 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome; |
| 45 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome.Overvi
ewLayoutFactoryDelegate; |
| 46 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChromePhone; |
| 47 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChromeTablet; |
| 48 import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost; |
| 49 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost; |
| 50 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior.Overv
iewModeObserver; |
| 51 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter; |
| 52 import org.chromium.chrome.browser.compositor.layouts.phone.StackLayout; |
| 53 import org.chromium.chrome.browser.cookies.CookiesFetcher; |
| 54 import org.chromium.chrome.browser.device.DeviceClassManager; |
| 55 import org.chromium.chrome.browser.document.DocumentUma; |
| 56 import org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkUtils; |
| 57 import org.chromium.chrome.browser.firstrun.FirstRunActivity; |
| 58 import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer; |
| 59 import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor; |
| 60 import org.chromium.chrome.browser.firstrun.FirstRunStatus; |
| 61 import org.chromium.chrome.browser.metrics.LaunchMetrics; |
| 62 import org.chromium.chrome.browser.metrics.UmaUtils; |
| 63 import org.chromium.chrome.browser.ntp.NativePageAssassin; |
| 64 import org.chromium.chrome.browser.omaha.OmahaClient; |
| 65 import org.chromium.chrome.browser.omnibox.AutocompleteController; |
| 66 import org.chromium.chrome.browser.partnercustomizations.HomepageManager; |
| 67 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomiza
tions; |
| 68 import org.chromium.chrome.browser.preferences.ChromePreferenceManager; |
| 69 import org.chromium.chrome.browser.preferences.ConnectionChangeReceiver; |
| 70 import org.chromium.chrome.browser.preferences.PrefServiceBridge; |
| 71 import org.chromium.chrome.browser.preferences.bandwidth.BandwidthReductionPrefe
rences; |
| 72 import org.chromium.chrome.browser.preferences.bandwidth.DataReductionPromoScree
n; |
| 73 import org.chromium.chrome.browser.signin.SigninPromoScreen; |
| 74 import org.chromium.chrome.browser.snackbar.undo.UndoBarPopupController; |
| 75 import org.chromium.chrome.browser.sync.SyncController; |
| 76 import org.chromium.chrome.browser.tab.ChromeTab; |
| 77 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; |
| 78 import org.chromium.chrome.browser.tabmodel.TabModel; |
| 79 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; |
| 80 import org.chromium.chrome.browser.tabmodel.TabModelObserver; |
| 81 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl; |
| 82 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver; |
| 83 import org.chromium.chrome.browser.tabmodel.TabModelUtils; |
| 84 import org.chromium.chrome.browser.tabmodel.TabWindowManager; |
| 85 import org.chromium.chrome.browser.toolbar.ToolbarControlContainer; |
| 86 import org.chromium.chrome.browser.toolbar.ToolbarHelper; |
| 87 import org.chromium.chrome.browser.util.FeatureUtilities; |
| 88 import org.chromium.chrome.browser.widget.emptybackground.EmptyBackgroundViewWra
pper; |
| 89 import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager; |
| 90 import org.chromium.content.browser.ContentVideoView; |
| 91 import org.chromium.content.browser.ContentViewCore; |
| 92 import org.chromium.content.browser.crypto.CipherFactory; |
| 93 import org.chromium.content.common.ContentSwitches; |
| 94 import org.chromium.content_public.browser.LoadUrlParams; |
| 95 import org.chromium.ui.base.DeviceFormFactor; |
| 96 import org.chromium.ui.base.PageTransition; |
| 97 |
| 98 /** |
| 99 * This is the main activity for ChromeMobile when not running in document mode.
All the tabs |
| 100 * are accessible via a chrome specific tab switching UI. |
| 101 */ |
| 102 public class ChromeTabbedActivity extends CompositorChromeActivity implements Ac
tionBarDelegate, |
| 103 OverviewModeObserver { |
| 104 |
| 105 private static final int FIRST_RUN_EXPERIENCE_RESULT = 101; |
| 106 |
| 107 private static final String TAG = "ChromeTabbedActivity"; |
| 108 |
| 109 private static final String HELP_URL_PREFIX = "https://support.google.com/ch
rome/"; |
| 110 |
| 111 private static final String FRE_RUNNING = "First run is running"; |
| 112 |
| 113 private static final String WINDOW_INDEX = "window_index"; |
| 114 |
| 115 // How long to delay closing the current tab when our app is minimized. Hav
e to delay this |
| 116 // so that we don't show the contents of the next tab while minimizing. |
| 117 private static final long CLOSE_TAB_ON_MINIMIZE_DELAY_MS = 500; |
| 118 |
| 119 // Maximum delay for initial tab creation. This is for homepage and NTP, not
previous tabs |
| 120 // restore. This is needed because we do not know when reading PartnerBrowse
rCustomizations |
| 121 // provider will be finished. |
| 122 private static final int INITIAL_TAB_CREATION_TIMEOUT_MS = 500; |
| 123 |
| 124 /** |
| 125 * Sending an intent with this extra sets the app into single process mode. |
| 126 * This is only used for testing, when certain tests want to force this beha
viour. |
| 127 */ |
| 128 public static final String INTENT_EXTRA_TEST_RENDER_PROCESS_LIMIT = "render_
process_limit"; |
| 129 |
| 130 /** |
| 131 * Sending an intent with this extra disable uploading of minidumps. |
| 132 * This is only used for testing, when certain tests want to force this beha
viour. |
| 133 */ |
| 134 public static final String INTENT_EXTRA_DISABLE_CRASH_DUMP_UPLOADING = |
| 135 "disable_crash_dump_uploading"; |
| 136 |
| 137 /** |
| 138 * Sending an intent with this action to Chrome will cause it to close all t
abs |
| 139 * (iff the --enable-test-intents command line flag is set). If a URL is sup
plied in the |
| 140 * intent data, this will be loaded and unaffected by the close all action. |
| 141 */ |
| 142 private static final String ACTION_CLOSE_TABS = |
| 143 "com.google.android.apps.chrome.ACTION_CLOSE_TABS"; |
| 144 |
| 145 private ToolbarHelper mToolbarHelper; |
| 146 |
| 147 private FindToolbarManager mFindToolbarManager; |
| 148 |
| 149 private UndoBarPopupController mUndoBarPopupController; |
| 150 |
| 151 private LayoutManagerChrome mLayoutManager; |
| 152 |
| 153 private ChromeAppMenuPropertiesDelegate mChromeAppMenuPropertiesDelegate; |
| 154 private AppMenuHandler mAppMenuHandler; |
| 155 |
| 156 private View mMenuAnchor; |
| 157 |
| 158 private ViewGroup mContentContainer; |
| 159 |
| 160 private ToolbarControlContainer mControlContainer; |
| 161 |
| 162 private TabModelSelectorImpl mTabModelSelectorImpl; |
| 163 private TabModelSelectorTabObserver mTabModelSelectorTabObserver; |
| 164 private TabModelObserver mTabModelObserver; |
| 165 |
| 166 private ConnectionChangeReceiver mConnectionChangeReceiver; |
| 167 |
| 168 private boolean mUIInitialized = false; |
| 169 |
| 170 private boolean mIsOnFirstRun = false; |
| 171 |
| 172 /** |
| 173 * Keeps track of whether or not a specific tab was created based on the sta
rtup intent. |
| 174 */ |
| 175 private boolean mCreatedTabOnStartup = false; |
| 176 |
| 177 // Whether or not chrome was launched with an intent to open a tab. |
| 178 private boolean mIntentWithEffect = false; |
| 179 |
| 180 // Time at which an intent was received and handled. |
| 181 private long mIntentHandlingTimeMs = 0; |
| 182 |
| 183 @Override |
| 184 public void initializeCompositor() { |
| 185 try { |
| 186 TraceEvent.begin("ChromeTabbedActivity.initializeCompositor"); |
| 187 super.initializeCompositor(); |
| 188 |
| 189 mTabModelSelectorImpl.onNativeLibraryReady(getTabContentManager()); |
| 190 |
| 191 mTabModelObserver = new EmptyTabModelObserver() { |
| 192 @Override |
| 193 public void didCloseTab(Tab tab) { |
| 194 closeIfNoTabsAndHomepageEnabled(); |
| 195 } |
| 196 |
| 197 @Override |
| 198 public void tabPendingClosure(Tab tab) { |
| 199 closeIfNoTabsAndHomepageEnabled(); |
| 200 } |
| 201 |
| 202 private void closeIfNoTabsAndHomepageEnabled() { |
| 203 // If the last tab is closed, and homepage is enabled, then
exit Chrome. |
| 204 if (HomepageManager.isHomepageEnabled(getApplicationContext(
)) |
| 205 && getTabModelSelector().getTotalTabCount() == 0) { |
| 206 finish(); |
| 207 } |
| 208 } |
| 209 |
| 210 @Override |
| 211 public void didAddTab(Tab tab, TabLaunchType type) { |
| 212 if (type == TabLaunchType.FROM_LONGPRESS_BACKGROUND |
| 213 && !DeviceClassManager.enableAnimations(getApplicati
onContext())) { |
| 214 Toast.makeText(getBaseContext(), |
| 215 R.string.open_in_new_tab_toast, |
| 216 Toast.LENGTH_SHORT).show(); |
| 217 } |
| 218 } |
| 219 }; |
| 220 for (TabModel model : mTabModelSelectorImpl.getModels()) { |
| 221 model.addObserver(mTabModelObserver); |
| 222 } |
| 223 |
| 224 Bundle state = getSavedInstanceState(); |
| 225 if (state != null && state.containsKey(FRE_RUNNING)) { |
| 226 mIsOnFirstRun = state.getBoolean(FRE_RUNNING); |
| 227 } |
| 228 } finally { |
| 229 TraceEvent.end("ChromeTabbedActivity.initializeCompositor"); |
| 230 } |
| 231 } |
| 232 |
| 233 private void refreshSignIn() { |
| 234 if (mIsOnFirstRun) return; |
| 235 android.util.Log.i(TAG, "in refreshSignIn before starting the sign-in pr
ocessor"); |
| 236 FirstRunSignInProcessor.start(this); |
| 237 } |
| 238 |
| 239 @Override |
| 240 public void onNewIntent(Intent intent) { |
| 241 mIntentHandlingTimeMs = SystemClock.uptimeMillis(); |
| 242 super.onNewIntent(intent); |
| 243 } |
| 244 |
| 245 @Override |
| 246 public void finishNativeInitialization() { |
| 247 try { |
| 248 TraceEvent.begin("ChromeTabbedActivity.finishNativeInitialization"); |
| 249 |
| 250 launchFirstRunExperience(); |
| 251 |
| 252 ChromePreferenceManager preferenceManager = ChromePreferenceManager.
getInstance(this); |
| 253 // Promos can only be shown when we start with ACTION_MAIN intent an
d |
| 254 // after FRE is complete. |
| 255 if (!mIntentWithEffect && FirstRunStatus.getFirstRunFlowComplete(thi
s)) { |
| 256 // Only show promos on the second oppurtunity. This is because w
e show FRE on the |
| 257 // first oppurtunity, and we don't want to show such content bac
k to back. |
| 258 if (preferenceManager.getPromosSkippedOnFirstStart()) { |
| 259 // Data reduction promo should be temporarily suppressed if
the sign in promo is |
| 260 // shown to avoid nagging users too much. |
| 261 if (!SigninPromoScreen.launchSigninPromoIfNeeded(this)) { |
| 262 DataReductionPromoScreen.launchDataReductionPromo(this); |
| 263 } |
| 264 } else { |
| 265 preferenceManager.setPromosSkippedOnFirstStart(true); |
| 266 } |
| 267 } |
| 268 |
| 269 refreshSignIn(); |
| 270 |
| 271 initializeUI(); |
| 272 |
| 273 // The dataset has already been created, we need to initialize our s
tate. |
| 274 mTabModelSelectorImpl.notifyChanged(); |
| 275 |
| 276 getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS, |
| 277 Window.PROGRESS_VISIBILITY_OFF); |
| 278 |
| 279 super.finishNativeInitialization(); |
| 280 |
| 281 if (getActivityTab() != null) { |
| 282 BandwidthReductionPreferences.launchDataReductionSSLInfoBar( |
| 283 this, getActivityTab().getWebContents()); |
| 284 } |
| 285 } finally { |
| 286 TraceEvent.end("ChromeTabbedActivity.finishNativeInitialization"); |
| 287 } |
| 288 } |
| 289 |
| 290 @Override |
| 291 public void onResumeWithNative() { |
| 292 super.onResumeWithNative(); |
| 293 CookiesFetcher.restoreCookies(this); |
| 294 } |
| 295 |
| 296 @Override |
| 297 public void onPauseWithNative() { |
| 298 mTabModelSelectorImpl.commitAllTabClosures(); |
| 299 CookiesFetcher.persistCookies(this); |
| 300 super.onPauseWithNative(); |
| 301 } |
| 302 |
| 303 @Override |
| 304 public void onStopWithNative() { |
| 305 super.onStopWithNative(); |
| 306 mTabModelSelectorImpl.saveState(); |
| 307 try { |
| 308 getConnectionChangeReceiver().unregisterReceiver(ChromeTabbedActivit
y.this); |
| 309 } catch (IllegalArgumentException e) { |
| 310 // This may happen when onStop get called very early in UI test. |
| 311 } |
| 312 |
| 313 // Dismiss the popup menu if it is showing. |
| 314 hideMenus(); |
| 315 } |
| 316 |
| 317 @Override |
| 318 public void onStartWithNative() { |
| 319 super.onStartWithNative(); |
| 320 // If we don't have a current tab, show the overview mode. |
| 321 if (getActivityTab() == null) mLayoutManager.showOverview(false); |
| 322 |
| 323 getConnectionChangeReceiver().registerReceiver(ChromeTabbedActivity.this
); |
| 324 |
| 325 resetSavedInstanceState(); |
| 326 |
| 327 if (FeatureUtilities.isDocumentModeEligible(this)) { |
| 328 DocumentUma.recordInDocumentMode(false); |
| 329 } |
| 330 } |
| 331 |
| 332 @Override |
| 333 public void onNewIntentWithNative(Intent intent) { |
| 334 try { |
| 335 TraceEvent.begin("ChromeTabbedActivity.onNewIntentWithNative"); |
| 336 |
| 337 super.onNewIntentWithNative(intent); |
| 338 if (CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_TEST_
INTENTS)) { |
| 339 handleDebugIntent(intent); |
| 340 } |
| 341 } finally { |
| 342 TraceEvent.end("ChromeTabbedActivity.onNewIntentWithNative"); |
| 343 } |
| 344 } |
| 345 |
| 346 private void handleDebugIntent(Intent intent) { |
| 347 if (ACTION_CLOSE_TABS.equals(intent.getAction())) { |
| 348 getTabModelSelector().closeAllTabs(); |
| 349 } else if (MemoryPressureListener.handleDebugIntent(ChromeTabbedActivity
.this, |
| 350 intent.getAction())) { |
| 351 // Handled. |
| 352 } |
| 353 } |
| 354 |
| 355 private static class StackLayoutFactory implements OverviewLayoutFactoryDele
gate { |
| 356 @Override |
| 357 public Layout createOverviewLayout(Context context, LayoutUpdateHost upd
ateHost, |
| 358 LayoutRenderHost renderHost, EventFilter eventFilter) { |
| 359 return new StackLayout(context, updateHost, renderHost, eventFilter)
; |
| 360 } |
| 361 } |
| 362 |
| 363 private void initializeUI() { |
| 364 try { |
| 365 TraceEvent.begin("ChromeTabbedActivity.initializeUI"); |
| 366 |
| 367 CommandLine commandLine = CommandLine.getInstance(); |
| 368 |
| 369 commandLine.appendSwitch(ContentSwitches.ENABLE_INSTANT_EXTENDED_API
); |
| 370 |
| 371 CompositorViewHolder compositorViewHolder = getCompositorViewHolder(
); |
| 372 if (DeviceFormFactor.isTablet(this)) { |
| 373 boolean enableTabSwitcher = |
| 374 CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABL
E_TABLET_TAB_STACK); |
| 375 mLayoutManager = new LayoutManagerChromeTablet(compositorViewHol
der, |
| 376 enableTabSwitcher ? new StackLayoutFactory() : null); |
| 377 } else { |
| 378 mLayoutManager = new LayoutManagerChromePhone(compositorViewHold
er, |
| 379 new StackLayoutFactory()); |
| 380 } |
| 381 |
| 382 mLayoutManager.addOverviewModeObserver(this); |
| 383 |
| 384 // TODO(yusufo): get rid of findViewById(R.id.url_bar). |
| 385 initializeCompositorContent(mLayoutManager, findViewById(R.id.url_ba
r), |
| 386 mContentContainer, mControlContainer); |
| 387 |
| 388 mTabModelSelectorImpl.setOverviewModeBehavior(mLayoutManager); |
| 389 |
| 390 mUndoBarPopupController.initialize(); |
| 391 |
| 392 // Adjust the content container if we're not entering fullscreen mod
e. |
| 393 if (getFullscreenManager() == null) { |
| 394 float controlHeight = getResources().getDimension(R.dimen.contro
l_container_height); |
| 395 ((FrameLayout.LayoutParams) mContentContainer.getLayoutParams())
.topMargin = |
| 396 (int) controlHeight; |
| 397 } |
| 398 |
| 399 // Bootstrap the first tab as it may have been created before initia
lizing the |
| 400 // fullscreen manager. |
| 401 if (mTabModelSelectorImpl != null && mTabModelSelectorImpl.getCurren
tTab() != null) { |
| 402 mTabModelSelectorImpl.getCurrentTab().setFullscreenManager(getFu
llscreenManager()); |
| 403 } |
| 404 |
| 405 mAppMenuHandler.addObserver(new AppMenuObserver() { |
| 406 @Override |
| 407 public void onMenuVisibilityChanged(boolean isVisible) { |
| 408 if (!isVisible) { |
| 409 mChromeAppMenuPropertiesDelegate.onMenuDismissed(); |
| 410 } |
| 411 } |
| 412 }); |
| 413 |
| 414 mFindToolbarManager = new FindToolbarManager(this, getTabModelSelect
or(), |
| 415 mToolbarHelper.getContextualMenuBar().getCustomSelectionActi
onModeCallback()); |
| 416 mControlContainer.setFindToolbarManager(mFindToolbarManager); |
| 417 if (getContextualSearchManager() != null) { |
| 418 getContextualSearchManager().setFindToolbarManager(mFindToolbarM
anager); |
| 419 } |
| 420 |
| 421 OnClickListener tabSwitcherClickHandler = new OnClickListener() { |
| 422 @Override |
| 423 public void onClick(View v) { |
| 424 toggleOverview(); |
| 425 } |
| 426 }; |
| 427 OnClickListener newTabClickHandler = new OnClickListener() { |
| 428 @Override |
| 429 public void onClick(View v) { |
| 430 // This assumes that the keyboard can not be seen at the sam
e time as the |
| 431 // newtab button on the toolbar. |
| 432 getCurrentTabCreator().launchNTP(); |
| 433 } |
| 434 }; |
| 435 OnClickListener bookmarkClickHandler = new OnClickListener() { |
| 436 @Override |
| 437 public void onClick(View v) { |
| 438 addOrEditBookmark(getActivityTab()); |
| 439 } |
| 440 }; |
| 441 |
| 442 mToolbarHelper.initializeControls(mFindToolbarManager, mLayoutManage
r, mLayoutManager, |
| 443 tabSwitcherClickHandler, newTabClickHandler, bookmarkClickHa
ndler, null); |
| 444 |
| 445 mMenuAnchor = findViewById(R.id.menu_anchor_stub); |
| 446 |
| 447 removeWindowBackground(); |
| 448 |
| 449 if (mIsTablet) { |
| 450 EmptyBackgroundViewWrapper bgViewWrapper = new EmptyBackgroundVi
ewWrapper( |
| 451 getTabModelSelector(), getTabCreator(false), ChromeTabbe
dActivity.this, |
| 452 mAppMenuHandler, mLayoutManager); |
| 453 bgViewWrapper.initialize(); |
| 454 } |
| 455 |
| 456 mLayoutManager.hideOverview(false); |
| 457 |
| 458 mUIInitialized = true; |
| 459 } finally { |
| 460 TraceEvent.end("ChromeTabbedActivity.initializeUI"); |
| 461 } |
| 462 } |
| 463 |
| 464 @Override |
| 465 public void initializeState() { |
| 466 // This method goes through 3 steps: |
| 467 // 1. Load the saved tab state (but don't start restoring the tabs yet). |
| 468 // 2. Process the Intent that this activity received and if that should
result in any |
| 469 // new tabs, create them. This is done after step 1 so that the new
tab gets |
| 470 // created after previous tab state was restored. |
| 471 // 3. If no tabs were created in any of the above steps, create an NTP,
otherwise |
| 472 // start asynchronous tab restore (loading the previously active tab
synchronously |
| 473 // if no new tabs created in step 2). |
| 474 |
| 475 // Only look at the original intent if this is not a "restoration" and w
e are allowed to |
| 476 // process intents. Any subsequent intents are carried through onNewInte
nt. |
| 477 try { |
| 478 TraceEvent.begin("ChromeTabbedActivity.initializeState"); |
| 479 |
| 480 super.initializeState(); |
| 481 |
| 482 Intent intent = getIntent(); |
| 483 |
| 484 if (!CipherFactory.getInstance().restoreFromBundle(getSavedInstanceS
tate())) { |
| 485 mTabModelSelectorImpl.clearEncryptedState(); |
| 486 } |
| 487 |
| 488 boolean noRestoreState = |
| 489 CommandLine.getInstance().hasSwitch(ChromeSwitches.NO_RESTOR
E_STATE); |
| 490 if (noRestoreState) { |
| 491 // Clear the state files because they are inconsistent and usele
ss from now on. |
| 492 mTabModelSelectorImpl.clearState(); |
| 493 } else if (!mIsOnFirstRun) { |
| 494 // State should be clear when we start first run and hence we do
not need to load |
| 495 // a previous state. This may change the current Model, watch ou
t for initialization |
| 496 // based on the model. |
| 497 mTabModelSelectorImpl.loadState(); |
| 498 } |
| 499 |
| 500 mIntentWithEffect = false; |
| 501 if ((mIsOnFirstRun || getSavedInstanceState() == null) && intent !=
null |
| 502 && !mIntentHandler.shouldIgnoreIntent(ChromeTabbedActivity.t
his, intent)) { |
| 503 mIntentWithEffect = mIntentHandler.onNewIntent(intent); |
| 504 } |
| 505 |
| 506 mCreatedTabOnStartup = getCurrentTabModel().getCount() > 0 |
| 507 || mTabModelSelectorImpl.getRestoredTabCount() > 0 |
| 508 || mIntentWithEffect; |
| 509 |
| 510 // We always need to try to restore tabs. The set of tabs might be e
mpty, but at least |
| 511 // it will trigger the notification that tab restore is complete whi
ch is needed by |
| 512 // other parts of Chrome such as sync. |
| 513 boolean activeTabBeingRestored = !mIntentWithEffect; |
| 514 mTabModelSelectorImpl.restoreTabs(activeTabBeingRestored); |
| 515 |
| 516 // Only create an initial tab if no tabs were restored and no intent
was handled. |
| 517 // Also, check whether the active tab was supposed to be restored an
d that the total |
| 518 // tab count is now non zero. If this is not the case, tab restore
failed and we need |
| 519 // to create a new tab as well. |
| 520 if (!mCreatedTabOnStartup |
| 521 || (activeTabBeingRestored && getTabModelSelector().getTotal
TabCount() == 0)) { |
| 522 // If homepage URI is not determined, due to PartnerBrowserCusto
mizations provider |
| 523 // async reading, then create a tab at the async reading finishe
d. If it takes |
| 524 // too long, just create NTP. |
| 525 PartnerBrowserCustomizations.setOnInitializeAsyncFinished( |
| 526 new Runnable() { |
| 527 @Override |
| 528 public void run() { |
| 529 createInitialTab(); |
| 530 } |
| 531 }, INITIAL_TAB_CREATION_TIMEOUT_MS); |
| 532 } |
| 533 |
| 534 RecordHistogram.recordBooleanHistogram( |
| 535 "MobileStartup.ColdStartupIntent", mIntentWithEffect); |
| 536 } finally { |
| 537 TraceEvent.end("ChromeTabbedActivity.initializeState"); |
| 538 } |
| 539 } |
| 540 |
| 541 /** |
| 542 * Create an initial tab for cold start without restored tabs. |
| 543 */ |
| 544 private void createInitialTab() { |
| 545 String url = HomepageManager.getHomepageUri(getApplicationContext()); |
| 546 if (TextUtils.isEmpty(url)) url = UrlConstants.NTP_URL; |
| 547 getTabCreator(false).createNewTab( |
| 548 new LoadUrlParams(url), TabLaunchType.FROM_MENU_OR_OVERVIEW, nul
l); |
| 549 } |
| 550 |
| 551 @Override |
| 552 public boolean onActivityResultWithNative(int requestCode, int resultCode, I
ntent data) { |
| 553 if (super.onActivityResultWithNative(requestCode, resultCode, data)) ret
urn true; |
| 554 |
| 555 if (requestCode == FIRST_RUN_EXPERIENCE_RESULT) { |
| 556 mIsOnFirstRun = false; |
| 557 if (resultCode == RESULT_OK) { |
| 558 refreshSignIn(); |
| 559 } else { |
| 560 if (data != null && data.getBooleanExtra( |
| 561 FirstRunActivity.RESULT_CLOSE_APP, false)) { |
| 562 getTabModelSelector().closeAllTabs(true); |
| 563 finish(); |
| 564 } else { |
| 565 launchFirstRunExperience(); |
| 566 } |
| 567 } |
| 568 return true; |
| 569 } |
| 570 return false; |
| 571 } |
| 572 |
| 573 @Override |
| 574 public void onOrientationChange(int orientation) { |
| 575 super.onOrientationChange(orientation); |
| 576 mToolbarHelper.onOrientationChange(); |
| 577 } |
| 578 |
| 579 @Override |
| 580 public void onAccessibilityModeChanged(boolean enabled) { |
| 581 super.onAccessibilityModeChanged(enabled); |
| 582 if (mToolbarHelper != null) mToolbarHelper.onAccessibilityStatusChanged(
enabled); |
| 583 |
| 584 if (mLayoutManager != null) { |
| 585 mLayoutManager.setEnableAnimations( |
| 586 DeviceClassManager.enableAnimations(getApplicationContext())
); |
| 587 } |
| 588 if (mIsTablet) { |
| 589 if (getCompositorViewHolder() != null) { |
| 590 getCompositorViewHolder().onAccessibilityStatusChanged(enabled); |
| 591 } |
| 592 } |
| 593 if (mLayoutManager != null && mLayoutManager.overviewVisible()) { |
| 594 mLayoutManager.hideOverview(false); |
| 595 } |
| 596 } |
| 597 |
| 598 /** |
| 599 * Internal class which performs the intent handling operations delegated by
IntentHandler. |
| 600 */ |
| 601 private class InternalIntentDelegate implements IntentHandler.IntentHandlerD
elegate { |
| 602 |
| 603 /** |
| 604 * Processes a url view intent. |
| 605 * |
| 606 * @param url The url from the intent. |
| 607 */ |
| 608 @Override |
| 609 public void processUrlViewIntent(String url, String headers, TabOpenType
tabOpenType, |
| 610 String externalAppId, int tabIdToBringToFront, Intent intent) { |
| 611 TabModel tabModel = getCurrentTabModel(); |
| 612 switch (tabOpenType) { |
| 613 case REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB: |
| 614 // Used by the bookmarks application. |
| 615 if (tabModel.getCount() > 0 && mUIInitialized |
| 616 && mLayoutManager.overviewVisible()) { |
| 617 mLayoutManager.hideOverview(true); |
| 618 } |
| 619 mTabModelSelectorImpl.tryToRestoreTabStateForUrl(url); |
| 620 int tabToBeClobberedIndex = TabModelUtils.getTabIndexByUrl(t
abModel, url); |
| 621 Tab tabToBeClobbered = tabModel.getTabAt(tabToBeClobberedInd
ex); |
| 622 if (tabToBeClobbered != null) { |
| 623 TabModelUtils.setIndex(tabModel, tabToBeClobberedIndex); |
| 624 tabToBeClobbered.reload(); |
| 625 RecordUserAction.record("MobileTabClobbered"); |
| 626 } else { |
| 627 launchIntent(url, headers, externalAppId, true, intent); |
| 628 } |
| 629 RecordUserAction.record("MobileReceivedExternalIntent"); |
| 630 LaunchMetrics.recordHomeScreenLaunchIntoTab(url); |
| 631 break; |
| 632 case REUSE_APP_ID_MATCHING_TAB_ELSE_NEW_TAB: |
| 633 launchIntent(url, headers, externalAppId, false, intent); |
| 634 RecordUserAction.record("MobileReceivedExternalIntent"); |
| 635 break; |
| 636 case BRING_TAB_TO_FRONT: |
| 637 mTabModelSelectorImpl.tryToRestoreTabStateForId(tabIdToBring
ToFront); |
| 638 |
| 639 int tabIndex = TabModelUtils.getTabIndexById(tabModel, tabId
ToBringToFront); |
| 640 if (tabIndex == TabModel.INVALID_TAB_INDEX) { |
| 641 TabModel otherModel = |
| 642 getTabModelSelector().getModel(!tabModel.isIncog
nito()); |
| 643 tabIndex = TabModelUtils.getTabIndexById(otherModel, tab
IdToBringToFront); |
| 644 if (tabIndex != TabModel.INVALID_TAB_INDEX) { |
| 645 getTabModelSelector().selectModel(otherModel.isIncog
nito()); |
| 646 TabModelUtils.setIndex(otherModel, tabIndex); |
| 647 } |
| 648 } else { |
| 649 TabModelUtils.setIndex(tabModel, tabIndex); |
| 650 } |
| 651 RecordUserAction.record("MobileReceivedExternalIntent"); |
| 652 break; |
| 653 case CLOBBER_CURRENT_TAB: |
| 654 // The browser triggered the intent. This happens when click
ing links which |
| 655 // can be handled by other applications (e.g. www.youtube.co
m links). |
| 656 ChromeTab currentTab = ChromeTab.fromTab(getActivityTab()); |
| 657 if (currentTab != null) { |
| 658 currentTab.getTabRedirectHandler().updateIntent(intent); |
| 659 int transitionType = PageTransition.LINK | PageTransitio
n.FROM_API; |
| 660 LoadUrlParams loadUrlParams = new LoadUrlParams(url, tra
nsitionType); |
| 661 loadUrlParams.setIntentReceivedTimestamp(mIntentHandling
TimeMs); |
| 662 currentTab.loadUrl(loadUrlParams); |
| 663 RecordUserAction.record("MobileTabClobbered"); |
| 664 } else { |
| 665 launchIntent(url, headers, externalAppId, true, intent); |
| 666 } |
| 667 break; |
| 668 case OPEN_NEW_TAB: |
| 669 launchIntent(url, headers, externalAppId, true, intent); |
| 670 RecordUserAction.record("MobileReceivedExternalIntent"); |
| 671 break; |
| 672 case OPEN_NEW_INCOGNITO_TAB: |
| 673 if (url == null || url.equals(UrlConstants.NTP_URL)) { |
| 674 if (TextUtils.equals(externalAppId, getPackageName())) { |
| 675 // Used by the Account management screen to open a n
ew incognito tab. |
| 676 // Account management screen collects its metrics se
parately. |
| 677 getTabCreator(true).launchUrl( |
| 678 UrlConstants.NTP_URL, TabLaunchType.FROM_MEN
U_OR_OVERVIEW); |
| 679 } else { |
| 680 getTabCreator(true).launchUrl( |
| 681 UrlConstants.NTP_URL, TabLaunchType.FROM_EXT
ERNAL_APP); |
| 682 RecordUserAction.record("MobileReceivedExternalInten
t"); |
| 683 } |
| 684 } else { |
| 685 if (TextUtils.equals(externalAppId, getPackageName())) { |
| 686 getTabCreator(true).launchUrl( |
| 687 url, TabLaunchType.FROM_LINK, intent, mInten
tHandlingTimeMs); |
| 688 } else { |
| 689 getTabCreator(true).launchUrlFromExternalApp(url, he
aders, |
| 690 externalAppId, true, intent, mIntentHandling
TimeMs); |
| 691 RecordUserAction.record("MobileReceivedExternalInten
t"); |
| 692 } |
| 693 } |
| 694 break; |
| 695 default: |
| 696 assert false : "Unknown TabOpenType: " + tabOpenType; |
| 697 break; |
| 698 } |
| 699 mToolbarHelper.setUrlBarFocus(false); |
| 700 } |
| 701 |
| 702 @Override |
| 703 public void processWebSearchIntent(String query) { |
| 704 Intent searchIntent = new Intent(Intent.ACTION_WEB_SEARCH); |
| 705 searchIntent.putExtra(SearchManager.QUERY, query); |
| 706 startActivity(searchIntent); |
| 707 } |
| 708 } |
| 709 |
| 710 @Override |
| 711 public boolean isStartedUpCorrectly(Intent intent) { |
| 712 if (FeatureUtilities.isDocumentMode(this)) { |
| 713 Log.e(TAG, "Discarding Intent: Starting ChromeTabbedActivity in Docu
ment mode"); |
| 714 return false; |
| 715 } |
| 716 return true; |
| 717 } |
| 718 |
| 719 @Override |
| 720 public void preInflationStartup() { |
| 721 super.preInflationStartup(); |
| 722 |
| 723 // Decide whether to record startup UMA histograms. This is done early
in the main |
| 724 // Activity.onCreate() to avoid recording navigation delays when they re
quire user input to |
| 725 // proceed. For example, FRE (First Run Experience) happens before the a
ctivity is created, |
| 726 // and triggers initialization of the native library. At the moment it s
eems safe to assume |
| 727 // that uninitialized native library is an indication of an application
start that is |
| 728 // followed by navigation immediately without user input. |
| 729 if (!LibraryLoader.isInitialized()) { |
| 730 UmaUtils.setRunningApplicationStart(true); |
| 731 } |
| 732 |
| 733 CommandLine commandLine = CommandLine.getInstance(); |
| 734 if (commandLine.hasSwitch(ContentSwitches.ENABLE_TEST_INTENTS) |
| 735 && getIntent() != null |
| 736 && getIntent().hasExtra( |
| 737 ChromeTabbedActivity.INTENT_EXTRA_TEST_RENDER_PROCESS_LI
MIT)) { |
| 738 int value = getIntent().getIntExtra( |
| 739 ChromeTabbedActivity.INTENT_EXTRA_TEST_RENDER_PROCESS_LIMIT,
-1); |
| 740 if (value != -1) { |
| 741 String[] args = new String[1]; |
| 742 args[0] = "--" + ChromeSwitches.RENDER_PROCESS_LIMIT |
| 743 + "=" + Integer.toString(value); |
| 744 commandLine.appendSwitchesAndArguments(args); |
| 745 } |
| 746 } |
| 747 |
| 748 commandLine.appendSwitch(ChromeSwitches.ENABLE_HIGH_END_UI_UNDO); |
| 749 |
| 750 supportRequestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY); |
| 751 |
| 752 // We are starting from history with a URL after data has been cleared.
On Samsung this |
| 753 // can happen after user clears data and clicks on a recents item on pre
-L devices. |
| 754 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP |
| 755 && getIntent().getData() != null |
| 756 && (getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_
HISTORY) != 0 |
| 757 && OmahaClient.isFreshInstallOrDataHasBeenCleared(getApplication
Context())) { |
| 758 getIntent().setData(null); |
| 759 } |
| 760 } |
| 761 |
| 762 @Override |
| 763 protected int getControlContainerLayoutId() { |
| 764 return R.layout.control_container; |
| 765 } |
| 766 |
| 767 @Override |
| 768 public void postInflationStartup() { |
| 769 super.postInflationStartup(); |
| 770 |
| 771 // Critical path for startup. Create the minimum objects needed |
| 772 // to allow a blank screen draw (without depending on any native code) |
| 773 // and then yield ASAP. |
| 774 createTabModelSelectorImpl(getSavedInstanceState()); |
| 775 |
| 776 if (isFinishing()) return; |
| 777 |
| 778 // Don't show the keyboard until user clicks in. |
| 779 getWindow().setSoftInputMode( |
| 780 WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN |
| 781 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); |
| 782 |
| 783 mContentContainer = (ViewGroup) findViewById(android.R.id.content); |
| 784 mControlContainer = (ToolbarControlContainer) findViewById(R.id.control_
container); |
| 785 |
| 786 mUndoBarPopupController = new UndoBarPopupController(this, mTabModelSele
ctorImpl, |
| 787 getSnackbarManager()); |
| 788 |
| 789 mChromeAppMenuPropertiesDelegate = new ChromeAppMenuPropertiesDelegate(t
his); |
| 790 mAppMenuHandler = new AppMenuHandler(ChromeTabbedActivity.this, |
| 791 mChromeAppMenuPropertiesDelegate, R.menu.main_menu); |
| 792 mToolbarHelper = new ToolbarHelper(this, mControlContainer, mAppMenuHand
ler, |
| 793 mChromeAppMenuPropertiesDelegate, getCompositorViewHolder().getI
nvalidator()); |
| 794 } |
| 795 |
| 796 /** |
| 797 * Launch the First Run flow to set up Chrome. |
| 798 * There are two different pathways that can occur: |
| 799 * 1) The First Run Experience activity is run, which walks the user through
the ToS, signing |
| 800 * in, and turning on UMA reporting. This happens in most cases. |
| 801 * 2) We automatically try to sign-in the user and skip the FRE activity, th
en ask the user to |
| 802 * turn on UMA reporting some time later using an InfoBar. This happens if
Chrome is opened |
| 803 * with an Intent to view a URL, or if we're on a Nexus device where the use
r has already |
| 804 * been exposed to the ToS and Privacy Notice. |
| 805 */ |
| 806 private void launchFirstRunExperience() { |
| 807 SyncController.get(this).setDelaySync(false); |
| 808 if (mIsOnFirstRun) { |
| 809 mTabModelSelectorImpl.clearState(); |
| 810 return; |
| 811 } |
| 812 |
| 813 final boolean isIntentActionMain = getIntent() != null |
| 814 && TextUtils.equals(getIntent().getAction(), Intent.ACTION_MAIN)
; |
| 815 android.util.Log.i(TAG, "begin FirstRunFlowSequencer.checkIfFirstRunIsNe
cessary"); |
| 816 final Intent freIntent = FirstRunFlowSequencer.checkIfFirstRunIsNecessar
y( |
| 817 this, getIntent(), isIntentActionMain); |
| 818 android.util.Log.i(TAG, "end FirstRunFlowSequencer.checkIfFirstRunIsNece
ssary"); |
| 819 if (freIntent == null) return; |
| 820 |
| 821 mIsOnFirstRun = true; |
| 822 |
| 823 // TODO(dtrainor): Investigate this further and revert once Android push
es fix? |
| 824 // Posting this due to Android bug where we apparently are stopping a |
| 825 // non-resumed activity. That statement looks incorrect, but need to no
t hit |
| 826 // the runtime exception here. |
| 827 mHandler.post(new Runnable() { |
| 828 @Override |
| 829 public void run() { |
| 830 startActivityForResult(freIntent, FIRST_RUN_EXPERIENCE_RESULT); |
| 831 } |
| 832 }); |
| 833 } |
| 834 |
| 835 @Override |
| 836 protected void onDeferredStartup() { |
| 837 try { |
| 838 TraceEvent.begin("ChromeTabbedActivity.onDeferredStartup"); |
| 839 super.onDeferredStartup(); |
| 840 |
| 841 mToolbarHelper.onDeferredStartup(); |
| 842 |
| 843 ActivityManager am = (ActivityManager) getSystemService(Context.ACTI
VITY_SERVICE); |
| 844 RecordHistogram.recordSparseSlowlyHistogram( |
| 845 "MemoryAndroid.DeviceMemoryClass", am.getMemoryClass()); |
| 846 |
| 847 AutocompleteController.nativePrefetchZeroSuggestResults(); |
| 848 } finally { |
| 849 TraceEvent.end("ChromeTabbedActivity.onDeferredStartup"); |
| 850 } |
| 851 } |
| 852 |
| 853 private void createTabModelSelectorImpl(Bundle savedInstanceState) { |
| 854 // We determine the model as soon as possible so every systems get initi
alized coherently. |
| 855 boolean startIncognito = savedInstanceState != null |
| 856 && savedInstanceState.getBoolean("is_incognito_selected", false)
; |
| 857 int index = savedInstanceState != null ? savedInstanceState.getInt(WINDO
W_INDEX, 0) : 0; |
| 858 mTabModelSelectorImpl = (TabModelSelectorImpl) |
| 859 TabWindowManager.getInstance().requestSelector(this, getWindowAn
droid(), index); |
| 860 if (mTabModelSelectorImpl == null) { |
| 861 Toast.makeText(this, getString(R.string.unsupported_number_of_window
s), |
| 862 Toast.LENGTH_LONG).show(); |
| 863 finish(); |
| 864 return; |
| 865 } |
| 866 mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(mTabModel
SelectorImpl) { |
| 867 |
| 868 private boolean mIsFirstPageLoadStart = true; |
| 869 |
| 870 @Override |
| 871 public void onPageLoadStarted(Tab tab) { |
| 872 // Discard startup navigation measurements when the user interfe
red and started the |
| 873 // 2nd navigation (in activity lifetime) in parallel. |
| 874 if (!mIsFirstPageLoadStart) { |
| 875 UmaUtils.setRunningApplicationStart(false); |
| 876 } else { |
| 877 mIsFirstPageLoadStart = false; |
| 878 } |
| 879 } |
| 880 }; |
| 881 |
| 882 if (startIncognito) mTabModelSelectorImpl.selectModel(true); |
| 883 setTabModelSelector(mTabModelSelectorImpl); |
| 884 } |
| 885 |
| 886 @Override |
| 887 public void terminateIncognitoSession() { |
| 888 getTabModelSelector().getModel(true).closeAllTabs(); |
| 889 } |
| 890 |
| 891 @Override |
| 892 public void onConfigurationChanged(Configuration newConfig) { |
| 893 hideMenus(); |
| 894 super.onConfigurationChanged(newConfig); |
| 895 } |
| 896 |
| 897 @Override |
| 898 public boolean onMenuOrKeyboardAction(final int id, boolean fromMenu) { |
| 899 final Tab currentTab = getActivityTab(); |
| 900 if (id == R.id.new_tab_menu_id) { |
| 901 Tab launchedTab = getTabCreator(false).launchUrl( |
| 902 UrlConstants.NTP_URL, |
| 903 fromMenu ? TabLaunchType.FROM_MENU_OR_OVERVIEW : TabLaunchTy
pe.FROM_KEYBOARD); |
| 904 RecordUserAction.record("MobileMenuNewTab"); |
| 905 RecordUserAction.record("MobileNewTabOpened"); |
| 906 if (mIsTablet && !fromMenu && !launchedTab.isHidden()) { |
| 907 mToolbarHelper.setUrlBarFocus(true); |
| 908 } |
| 909 } else if (id == R.id.new_incognito_tab_menu_id) { |
| 910 if (PrefServiceBridge.getInstance().isIncognitoModeEnabled()) { |
| 911 // This action must be recorded before opening the incognito tab
since UMA actions |
| 912 // are dropped when an incognito tab is open. |
| 913 RecordUserAction.record("MobileMenuNewIncognitoTab"); |
| 914 RecordUserAction.record("MobileNewTabOpened"); |
| 915 Tab launchedTab = getTabCreator(true).launchUrl( |
| 916 UrlConstants.NTP_URL, |
| 917 fromMenu ? TabLaunchType.FROM_MENU_OR_OVERVIEW |
| 918 : TabLaunchType.FROM_KEYBOARD); |
| 919 if (mIsTablet && !fromMenu && !launchedTab.isHidden()) { |
| 920 mToolbarHelper.setUrlBarFocus(true); |
| 921 } |
| 922 } |
| 923 } else if (id == R.id.all_bookmarks_menu_id) { |
| 924 if (currentTab != null) { |
| 925 getCompositorViewHolder().hideKeyboard(new Runnable() { |
| 926 @Override |
| 927 public void run() { |
| 928 if (!EnhancedBookmarkUtils.showEnhancedBookmarkIfEnabled
( |
| 929 ChromeTabbedActivity.this)) { |
| 930 currentTab.loadUrl(new LoadUrlParams( |
| 931 UrlConstants.BOOKMARKS_URL, |
| 932 PageTransition.AUTO_BOOKMARK)); |
| 933 } |
| 934 } |
| 935 }); |
| 936 RecordUserAction.record("MobileMenuAllBookmarks"); |
| 937 } |
| 938 } else if (id == R.id.recent_tabs_menu_id) { |
| 939 if (currentTab != null) { |
| 940 currentTab.loadUrl(new LoadUrlParams( |
| 941 UrlConstants.RECENT_TABS_URL, |
| 942 PageTransition.AUTO_BOOKMARK)); |
| 943 RecordUserAction.record("MobileMenuOpenTabs"); |
| 944 } |
| 945 } else if (id == R.id.close_all_tabs_menu_id) { |
| 946 // Close both incognito and normal tabs |
| 947 getTabModelSelector().closeAllTabs(); |
| 948 RecordUserAction.record("MobileMenuCloseAllTabs"); |
| 949 } else if (id == R.id.close_all_incognito_tabs_menu_id) { |
| 950 // Close only incognito tabs |
| 951 getTabModelSelector().getModel(true).closeAllTabs(); |
| 952 // TODO(nileshagrawal) Record unique action for this. See bug http:/
/b/5542946. |
| 953 RecordUserAction.record("MobileMenuCloseAllTabs"); |
| 954 } else if (id == R.id.find_in_page_id) { |
| 955 mFindToolbarManager.showToolbar(); |
| 956 if (fromMenu) { |
| 957 RecordUserAction.record("MobileMenuFindInPage"); |
| 958 } else { |
| 959 RecordUserAction.record("MobileShortcutFindInPage"); |
| 960 } |
| 961 } else if (id == R.id.show_menu) { |
| 962 showMenu(); |
| 963 } else if (id == R.id.focus_url_bar) { |
| 964 boolean isUrlBarVisible = !mLayoutManager.overviewVisible() |
| 965 && (!mIsTablet || getCurrentTabModel().getCount() != 0); |
| 966 if (isUrlBarVisible) { |
| 967 mToolbarHelper.setUrlBarFocus(true); |
| 968 } |
| 969 } else { |
| 970 return super.onMenuOrKeyboardAction(id, fromMenu); |
| 971 } |
| 972 return true; |
| 973 } |
| 974 |
| 975 @Override |
| 976 public boolean handleBackPressed() { |
| 977 if (!mUIInitialized) return false; |
| 978 final Tab currentTab = getActivityTab(); |
| 979 |
| 980 if (currentTab == null) { |
| 981 if (mToolbarHelper.back()) { |
| 982 RecordUserAction.record("SystemBackForNavigation"); |
| 983 RecordUserAction.record("MobileTabClobbered"); |
| 984 } else { |
| 985 moveTaskToBack(true); |
| 986 } |
| 987 RecordUserAction.record("SystemBack"); |
| 988 return true; |
| 989 } |
| 990 |
| 991 // If we are in overview mode and not a tablet, then leave overview mode
on back. |
| 992 if (mLayoutManager.overviewVisible() && !mIsTablet) { |
| 993 mLayoutManager.hideOverview(true); |
| 994 // TODO(benm): Record any user metrics in this case? |
| 995 return true; |
| 996 } |
| 997 |
| 998 if (isFullscreenVideoPlaying()) { |
| 999 ContentVideoView.getContentVideoView().exitFullscreen(false); |
| 1000 return true; |
| 1001 } |
| 1002 |
| 1003 if (getFullscreenManager().getPersistentFullscreenMode()) { |
| 1004 getFullscreenManager().setPersistentFullscreenMode(false); |
| 1005 return true; |
| 1006 } |
| 1007 |
| 1008 if (!mToolbarHelper.back()) { |
| 1009 final TabLaunchType type = currentTab.getLaunchType(); |
| 1010 final String associatedApp = currentTab.getAppAssociatedWith(); |
| 1011 final int parentId = currentTab.getParentId(); |
| 1012 final boolean helpUrl = currentTab.getUrl().startsWith(HELP_URL_PREF
IX); |
| 1013 |
| 1014 // If the current tab url is HELP_URL, then the back button should c
lose the tab to |
| 1015 // get back to the previous state. The reason for startsWith check i
s that the |
| 1016 // actual redirected URL is a different system language based help u
rl. |
| 1017 if (type == TabLaunchType.FROM_MENU_OR_OVERVIEW && helpUrl) { |
| 1018 getCurrentTabModel().closeTab(currentTab); |
| 1019 return true; |
| 1020 } |
| 1021 |
| 1022 // [true]: Reached the bottom of the back stack on a tab the user di
d not explicitly |
| 1023 // create (i.e. it was created by an external app or opening a link
in background, etc). |
| 1024 // [false]: Reached the bottom of the back stack on a tab that the u
ser explicitly |
| 1025 // created (e.g. selecting "new tab" from menu). |
| 1026 final boolean shouldCloseTab = type == TabLaunchType.FROM_LINK |
| 1027 || type == TabLaunchType.FROM_EXTERNAL_APP |
| 1028 || type == TabLaunchType.FROM_LONGPRESS_FOREGROUND |
| 1029 || type == TabLaunchType.FROM_LONGPRESS_BACKGROUND |
| 1030 || (type == TabLaunchType.FROM_RESTORE && parentId != Tab.IN
VALID_TAB_ID); |
| 1031 |
| 1032 // Minimize the app if either: |
| 1033 // - we decided not to close the tab |
| 1034 // - we decided to close the tab, but it was opened by an external a
pp, so we will go |
| 1035 // exit Chrome on top of closing the tab |
| 1036 final boolean minimizeApp = !shouldCloseTab || (type == TabLaunchTyp
e.FROM_EXTERNAL_APP |
| 1037 && !TextUtils.equals(associatedApp, getPackageName())); |
| 1038 |
| 1039 if (minimizeApp) { |
| 1040 moveTaskToBack(true); |
| 1041 if (shouldCloseTab) { |
| 1042 // In the case of closing a tab upon minimalization, don't a
llow the close |
| 1043 // action to happen until after our app is minimized to make
sure we don't get a |
| 1044 // brief glimpse of the newly active tab before we exit Chro
me. |
| 1045 mHandler.postDelayed(new Runnable() { |
| 1046 @Override |
| 1047 public void run() { |
| 1048 getCurrentTabModel().closeTab(currentTab, false, tru
e, false); |
| 1049 } |
| 1050 }, CLOSE_TAB_ON_MINIMIZE_DELAY_MS); |
| 1051 } |
| 1052 } else if (shouldCloseTab) { |
| 1053 getCurrentTabModel().closeTab(currentTab, true, false, false); |
| 1054 } |
| 1055 } else { |
| 1056 RecordUserAction.record("SystemBackForNavigation"); |
| 1057 RecordUserAction.record("MobileTabClobbered"); |
| 1058 } |
| 1059 RecordUserAction.record("SystemBack"); |
| 1060 |
| 1061 return true; |
| 1062 } |
| 1063 |
| 1064 /** |
| 1065 * Launch a URL from an intent. |
| 1066 * |
| 1067 * @param url The url from the intent. |
| 1068 * @param headers Optional headers to be sent when opening the URL. |
| 1069 * @param externalAppId External app id. |
| 1070 * @param forceNewTab Whether to force the URL to be launched in a new tab
or to fall |
| 1071 * back to the default behavior for making that determi
nation. |
| 1072 * @param intent The original intent. |
| 1073 */ |
| 1074 private void launchIntent(String url, String headers, String externalAppId, |
| 1075 boolean forceNewTab, Intent intent) { |
| 1076 if (mUIInitialized) { |
| 1077 mLayoutManager.hideOverview(false); |
| 1078 mToolbarHelper.finishAnimations(); |
| 1079 } |
| 1080 if (TextUtils.equals(externalAppId, getPackageName())) { |
| 1081 // If the intent was launched by chrome, open the new tab in the cur
rent model. |
| 1082 // Using FROM_LINK ensures the tab is parented to the current tab, w
hich allows |
| 1083 // the back button to close these tabs and restore selection to the
previous tab. |
| 1084 getCurrentTabCreator().launchUrl(url, TabLaunchType.FROM_LINK, inten
t, |
| 1085 mIntentHandlingTimeMs); |
| 1086 } else { |
| 1087 getTabCreator(false).launchUrlFromExternalApp(url, headers, external
AppId, |
| 1088 forceNewTab, intent, mIntentHandlingTimeMs); |
| 1089 } |
| 1090 } |
| 1091 |
| 1092 private void toggleOverview() { |
| 1093 Tab currentTab = getActivityTab(); |
| 1094 ContentViewCore contentViewCore = |
| 1095 currentTab != null ? currentTab.getContentViewCore() : null; |
| 1096 |
| 1097 if (!mLayoutManager.overviewVisible()) { |
| 1098 getCompositorViewHolder().hideKeyboard(new Runnable() { |
| 1099 @Override |
| 1100 public void run() { |
| 1101 mLayoutManager.showOverview(true); |
| 1102 } |
| 1103 }); |
| 1104 if (contentViewCore != null) { |
| 1105 contentViewCore.setAccessibilityState(false); |
| 1106 } |
| 1107 } else if (getCurrentTabModel().getCount() != 0) { |
| 1108 // Don't hide overview if current tab stack is empty() |
| 1109 mLayoutManager.hideOverview(true); |
| 1110 |
| 1111 // hideOverview could change the current tab. Update the local vari
ables. |
| 1112 currentTab = getActivityTab(); |
| 1113 contentViewCore = currentTab != null ? currentTab.getContentViewCore
() : null; |
| 1114 |
| 1115 if (contentViewCore != null) { |
| 1116 contentViewCore.setAccessibilityState(true); |
| 1117 } |
| 1118 } |
| 1119 } |
| 1120 |
| 1121 @Override |
| 1122 public void onSaveInstanceState(Bundle outState) { |
| 1123 super.onSaveInstanceState(outState); |
| 1124 CipherFactory.getInstance().saveToBundle(outState); |
| 1125 outState.putBoolean("is_incognito_selected", getCurrentTabModel().isInco
gnito()); |
| 1126 outState.putBoolean(FRE_RUNNING, mIsOnFirstRun); |
| 1127 outState.putInt(WINDOW_INDEX, |
| 1128 TabWindowManager.getInstance().getIndexForWindow(this)); |
| 1129 } |
| 1130 |
| 1131 @Override |
| 1132 public void onDestroyInternal() { |
| 1133 if (mLayoutManager != null) mLayoutManager.removeOverviewModeObserver(th
is); |
| 1134 if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.d
estroy(); |
| 1135 if (mTabModelObserver != null) { |
| 1136 for (TabModel model : mTabModelSelectorImpl.getModels()) { |
| 1137 model.removeObserver(mTabModelObserver); |
| 1138 } |
| 1139 } |
| 1140 if (mToolbarHelper != null) mToolbarHelper.destroy(); |
| 1141 if (mUndoBarPopupController != null) mUndoBarPopupController.destroy(); |
| 1142 super.onDestroyInternal(); |
| 1143 } |
| 1144 |
| 1145 @Override |
| 1146 public void onTrimMemory(int level) { |
| 1147 super.onTrimMemory(level); |
| 1148 // The conditions are expressed using ranges to capture intermediate lev
els possibly added |
| 1149 // to the API in the future. |
| 1150 if ((level >= TRIM_MEMORY_RUNNING_LOW && level < TRIM_MEMORY_UI_HIDDEN) |
| 1151 || level >= TRIM_MEMORY_MODERATE) { |
| 1152 NativePageAssassin.getInstance().freezeAllHiddenPages(); |
| 1153 } |
| 1154 } |
| 1155 |
| 1156 @Override |
| 1157 public boolean dispatchKeyEvent(KeyEvent event) { |
| 1158 Boolean result = KeyboardShortcuts.dispatchKeyEvent(event, this, mUIInit
ialized); |
| 1159 return result != null ? result : super.dispatchKeyEvent(event); |
| 1160 } |
| 1161 |
| 1162 @Override |
| 1163 public boolean onKeyDown(int keyCode, KeyEvent event) { |
| 1164 if (!mUIInitialized) return false; |
| 1165 boolean isCurrentTabVisible = !mLayoutManager.overviewVisible() |
| 1166 && (!mIsTablet || getCurrentTabModel().getCount() != 0); |
| 1167 return KeyboardShortcuts.onKeyDown(event, this, isCurrentTabVisible, tru
e) |
| 1168 || super.onKeyDown(keyCode, event); |
| 1169 } |
| 1170 |
| 1171 private void hideMenus() { |
| 1172 if (mAppMenuHandler != null) mAppMenuHandler.hideAppMenu(); |
| 1173 } |
| 1174 |
| 1175 private ConnectionChangeReceiver getConnectionChangeReceiver() { |
| 1176 if (mConnectionChangeReceiver == null) { |
| 1177 mConnectionChangeReceiver = new ConnectionChangeReceiver(); |
| 1178 } |
| 1179 return mConnectionChangeReceiver; |
| 1180 } |
| 1181 |
| 1182 /** |
| 1183 * Sets the top margin of the control container. |
| 1184 * |
| 1185 * @param margin The new top margin of the control container. |
| 1186 */ |
| 1187 @Override |
| 1188 public void setControlTopMargin(int margin) { |
| 1189 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) |
| 1190 mControlContainer.getLayoutParams(); |
| 1191 lp.topMargin = margin; |
| 1192 mControlContainer.setLayoutParams(lp); |
| 1193 } |
| 1194 |
| 1195 /** |
| 1196 * @return The top margin of the control container. |
| 1197 */ |
| 1198 @Override |
| 1199 public int getControlTopMargin() { |
| 1200 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) |
| 1201 mControlContainer.getLayoutParams(); |
| 1202 return lp.topMargin; |
| 1203 } |
| 1204 |
| 1205 @Override |
| 1206 public void setActionBarBackgroundVisibility(boolean visible) { |
| 1207 int visibility = visible ? View.VISIBLE : View.GONE; |
| 1208 findViewById(R.id.action_bar_black_background).setVisibility(visibility)
; |
| 1209 } |
| 1210 |
| 1211 @VisibleForTesting |
| 1212 public View getTabsView() { |
| 1213 return getCompositorViewHolder(); |
| 1214 } |
| 1215 |
| 1216 @VisibleForTesting |
| 1217 public LayoutManagerChrome getLayoutManager() { |
| 1218 return (LayoutManagerChrome) getCompositorViewHolder().getLayoutManager(
); |
| 1219 } |
| 1220 |
| 1221 @VisibleForTesting |
| 1222 public Layout getOverviewListLayout() { |
| 1223 return getLayoutManager().getOverviewListLayout(); |
| 1224 } |
| 1225 |
| 1226 @Override |
| 1227 public boolean isOverlayVisible() { |
| 1228 return getCompositorViewHolder() != null && !getCompositorViewHolder().i
sTabInteractive(); |
| 1229 } |
| 1230 |
| 1231 @Override |
| 1232 public boolean mayShowUpdateInfoBar() { |
| 1233 return !isOverlayVisible(); |
| 1234 } |
| 1235 |
| 1236 // App Menu related code ---------------------------------------------------
-------------------- |
| 1237 |
| 1238 @Override |
| 1239 @VisibleForTesting |
| 1240 public AppMenuHandler getAppMenuHandler() { |
| 1241 return mAppMenuHandler; |
| 1242 } |
| 1243 |
| 1244 @Override |
| 1245 public boolean shouldShowAppMenu() { |
| 1246 // The popup menu relies on the model created during the full UI initial
ization, so do not |
| 1247 // attempt to show the menu until the UI creation has finished. |
| 1248 if (!mUIInitialized) return false; |
| 1249 |
| 1250 // Do not show the menu if we are in find in page view. |
| 1251 if (mFindToolbarManager != null && mFindToolbarManager.isShowing() && !m
IsTablet) { |
| 1252 return false; |
| 1253 } |
| 1254 |
| 1255 return super.shouldShowAppMenu(); |
| 1256 } |
| 1257 |
| 1258 private boolean showMenu() { |
| 1259 if (!mUIInitialized || isFullscreenVideoPlaying()) return false; |
| 1260 |
| 1261 // The following will deduct the status bar height from the screen heigh
t and this |
| 1262 // is used as the height for the menu anchor. This fixes the bug where c
licking |
| 1263 // inside a search box on google.com causes the menu to appear on top as
keyboard |
| 1264 // reduces the view height. |
| 1265 int displayHeight = getResources().getDisplayMetrics().heightPixels; |
| 1266 |
| 1267 Rect rect = new Rect(); |
| 1268 this.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); |
| 1269 int statusBarHeight = rect.top; |
| 1270 mMenuAnchor.setY((displayHeight - statusBarHeight)); |
| 1271 mAppMenuHandler.showAppMenu(mMenuAnchor, true, false); |
| 1272 return true; |
| 1273 } |
| 1274 |
| 1275 private boolean isFullscreenVideoPlaying() { |
| 1276 View view = ContentVideoView.getContentVideoView(); |
| 1277 return view != null && view.getContext() == this; |
| 1278 } |
| 1279 |
| 1280 @Override |
| 1281 public boolean isInOverviewMode() { |
| 1282 return mLayoutManager.overviewVisible(); |
| 1283 } |
| 1284 |
| 1285 @Override |
| 1286 public boolean hasDoneFirstDraw() { |
| 1287 return mToolbarHelper.hasDoneFirstDraw(); |
| 1288 } |
| 1289 |
| 1290 @Override |
| 1291 protected IntentHandlerDelegate createIntentHandlerDelegate() { |
| 1292 return new InternalIntentDelegate(); |
| 1293 } |
| 1294 |
| 1295 @Override |
| 1296 public void onOverviewModeStartedShowing(boolean showToolbar) { |
| 1297 if (mFindToolbarManager != null) mFindToolbarManager.hideToolbar(); |
| 1298 } |
| 1299 |
| 1300 @Override |
| 1301 public void onSceneChange(Layout layout) { |
| 1302 super.onSceneChange(layout); |
| 1303 if (!layout.shouldDisplayContentOverlay()) mTabModelSelectorImpl.onTabsV
iewShown(); |
| 1304 } |
| 1305 |
| 1306 @Override |
| 1307 public void onOverviewModeFinishedShowing() { } |
| 1308 |
| 1309 @Override |
| 1310 public void onOverviewModeStartedHiding(boolean showToolbar, boolean delayAn
imation) {} |
| 1311 |
| 1312 @Override |
| 1313 public void onOverviewModeFinishedHiding() {} |
| 1314 } |
OLD | NEW |