| Index: chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..482dd8bb555ab739e272604d9b63b1bb7b0fac50
|
| --- /dev/null
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java
|
| @@ -0,0 +1,242 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.chrome.browser;
|
| +
|
| +import android.app.Activity;
|
| +import android.content.SharedPreferences;
|
| +import android.provider.Settings;
|
| +
|
| +import org.chromium.base.ApplicationState;
|
| +import org.chromium.base.ApplicationStatus;
|
| +import org.chromium.base.ApplicationStatus.ApplicationStateListener;
|
| +import org.chromium.base.ContextUtils;
|
| +import org.chromium.base.ThreadUtils;
|
| +import org.chromium.base.VisibleForTesting;
|
| +import org.chromium.base.metrics.RecordHistogram;
|
| +import org.chromium.chrome.browser.accessibility.FontSizePrefs;
|
| +import org.chromium.chrome.browser.browsing_data.BrowsingDataType;
|
| +import org.chromium.chrome.browser.browsing_data.TimePeriod;
|
| +import org.chromium.chrome.browser.metrics.UmaUtils;
|
| +import org.chromium.chrome.browser.metrics.VariationsSession;
|
| +import org.chromium.chrome.browser.notifications.NotificationPlatformBridge;
|
| +import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
|
| +import org.chromium.chrome.browser.preferences.PrefServiceBridge;
|
| +import org.chromium.chrome.browser.share.ShareHelper;
|
| +import org.chromium.chrome.browser.tabmodel.TabModelSelector;
|
| +import org.chromium.chrome.browser.util.FeatureUtilities;
|
| +import org.chromium.content.browser.ChildProcessLauncher;
|
| +
|
| +import java.lang.ref.WeakReference;
|
| +import java.util.Locale;
|
| +
|
| +/**
|
| + * Tracks the foreground session state for the Chrome activities.
|
| + */
|
| +public class ChromeActivitySessionTracker {
|
| +
|
| + private static final String PREF_LOCALE = "locale";
|
| +
|
| + private static ChromeActivitySessionTracker sInstance;
|
| +
|
| + private final PowerBroadcastReceiver mPowerBroadcastReceiver = new PowerBroadcastReceiver();
|
| +
|
| + // Used to trigger variation changes (such as seed fetches) upon application foregrounding.
|
| + private VariationsSession mVariationsSession;
|
| +
|
| + private ChromeApplication mApplication;
|
| + private boolean mIsInitialized;
|
| + private boolean mIsStarted;
|
| + private boolean mIsFinishedCachingNativeFlags;
|
| +
|
| + /**
|
| + * @return The activity session tracker for Chrome.
|
| + */
|
| + public static ChromeActivitySessionTracker getInstance() {
|
| + ThreadUtils.assertOnUiThread();
|
| +
|
| + if (sInstance == null) sInstance = new ChromeActivitySessionTracker();
|
| + return sInstance;
|
| + }
|
| +
|
| + /**
|
| + * Constructor exposed for extensibility only.
|
| + * @see #getInstance()
|
| + */
|
| + protected ChromeActivitySessionTracker() {
|
| + mApplication = (ChromeApplication) ContextUtils.getApplicationContext();
|
| + }
|
| +
|
| + /**
|
| + * Handle any initialization that occurs once native has been loaded.
|
| + */
|
| + public void initializeWithNative() {
|
| + ThreadUtils.assertOnUiThread();
|
| +
|
| + if (mIsInitialized) return;
|
| + mIsInitialized = true;
|
| + assert !mIsStarted;
|
| +
|
| + ApplicationStatus.registerApplicationStateListener(createApplicationStateListener());
|
| + mVariationsSession = mApplication.createVariationsSession();
|
| + }
|
| +
|
| + /**
|
| + * Each top-level activity (those extending {@link ChromeActivity}) should call this during
|
| + * its onStart phase. When called for the first time, this marks the beginning of a foreground
|
| + * session and calls onForegroundSessionStart(). Subsequent calls are noops until
|
| + * onForegroundSessionEnd() is called, to handle changing top-level Chrome activities in one
|
| + * foreground session.
|
| + */
|
| + public void onStartWithNative() {
|
| + ThreadUtils.assertOnUiThread();
|
| +
|
| + if (mIsStarted) return;
|
| + mIsStarted = true;
|
| +
|
| + assert mIsInitialized;
|
| +
|
| + onForegroundSessionStart();
|
| + cacheNativeFlags();
|
| + }
|
| +
|
| + /**
|
| + * Called when a top-level Chrome activity (ChromeTabbedActivity, FullscreenActivity) is
|
| + * started in foreground. It will not be called again when other Chrome activities take over
|
| + * (see onStart()), that is, when correct activity calls startActivity() for another Chrome
|
| + * activity.
|
| + */
|
| + private void onForegroundSessionStart() {
|
| + UmaUtils.recordForegroundStartTime();
|
| + ChildProcessLauncher.onBroughtToForeground();
|
| + updatePasswordEchoState();
|
| + FontSizePrefs.getInstance(mApplication).onSystemFontScaleChanged();
|
| + updateAcceptLanguages();
|
| + mVariationsSession.start(mApplication);
|
| + mPowerBroadcastReceiver.onForegroundSessionStart();
|
| +
|
| + // Track the ratio of Chrome startups that are caused by notification clicks.
|
| + // TODO(johnme): Add other reasons (and switch to recordEnumeratedHistogram).
|
| + RecordHistogram.recordBooleanHistogram(
|
| + "Startup.BringToForegroundReason",
|
| + NotificationPlatformBridge.wasNotificationRecentlyClicked());
|
| + }
|
| +
|
| + /**
|
| + * Called when last of Chrome activities is stopped, ending the foreground session. This will
|
| + * not be called when a Chrome activity is stopped because another Chrome activity takes over.
|
| + * This is ensured by ActivityStatus, which switches to track new activity when its started and
|
| + * will not report the old one being stopped (see createStateListener() below).
|
| + */
|
| + private void onForegroundSessionEnd() {
|
| + if (!mIsStarted) return;
|
| + ChromeApplication.flushPersistentData();
|
| + mIsStarted = false;
|
| + mPowerBroadcastReceiver.onForegroundSessionEnd();
|
| +
|
| + ChildProcessLauncher.onSentToBackground();
|
| + IntentHandler.clearPendingReferrer();
|
| + IntentHandler.clearPendingIncognitoUrl();
|
| +
|
| + int totalTabCount = 0;
|
| + for (WeakReference<Activity> reference : ApplicationStatus.getRunningActivities()) {
|
| + Activity activity = reference.get();
|
| + if (activity instanceof ChromeActivity) {
|
| + TabModelSelector tabModelSelector =
|
| + ((ChromeActivity) activity).getTabModelSelector();
|
| + if (tabModelSelector != null) {
|
| + totalTabCount += tabModelSelector.getTotalTabCount();
|
| + }
|
| + }
|
| + }
|
| + RecordHistogram.recordCountHistogram(
|
| + "Tab.TotalTabCount.BeforeLeavingApp", totalTabCount);
|
| + }
|
| +
|
| + private void onForegroundActivityDestroyed() {
|
| + if (ApplicationStatus.isEveryActivityDestroyed()) {
|
| + // These will all be re-initialized when a new Activity starts / upon next use.
|
| + PartnerBrowserCustomizations.destroy();
|
| + ShareHelper.clearSharedImages();
|
| + }
|
| + }
|
| +
|
| + private ApplicationStateListener createApplicationStateListener() {
|
| + return new ApplicationStateListener() {
|
| + @Override
|
| + public void onApplicationStateChange(int newState) {
|
| + if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES) {
|
| + onForegroundSessionEnd();
|
| + } else if (newState == ApplicationState.HAS_DESTROYED_ACTIVITIES) {
|
| + onForegroundActivityDestroyed();
|
| + }
|
| + }
|
| + };
|
| + }
|
| +
|
| + /**
|
| + * Update the accept languages after changing Android locale setting. Doing so kills the
|
| + * Activities but it doesn't kill the Application, so this should be called in
|
| + * {@link #onStart} instead of {@link #initialize}.
|
| + */
|
| + private void updateAcceptLanguages() {
|
| + PrefServiceBridge instance = PrefServiceBridge.getInstance();
|
| + String localeString = Locale.getDefault().toString(); // ex) en_US, de_DE, zh_CN_#Hans
|
| + if (hasLocaleChanged(localeString)) {
|
| + instance.resetAcceptLanguages(localeString);
|
| + // Clear cache so that accept-languages change can be applied immediately.
|
| + // TODO(changwan): The underlying BrowsingDataRemover::Remove() is an asynchronous call.
|
| + // So cache-clearing may not be effective if URL rendering can happen before
|
| + // OnBrowsingDataRemoverDone() is called, in which case we may have to reload as well.
|
| + // Check if it can happen.
|
| + instance.clearBrowsingData(
|
| + null, new int[]{ BrowsingDataType.CACHE }, TimePeriod.ALL_TIME);
|
| + }
|
| + }
|
| +
|
| + private boolean hasLocaleChanged(String newLocale) {
|
| + String previousLocale = ContextUtils.getAppSharedPreferences().getString(
|
| + PREF_LOCALE, "");
|
| +
|
| + if (!previousLocale.equals(newLocale)) {
|
| + SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
|
| + SharedPreferences.Editor editor = prefs.edit();
|
| + editor.putString(PREF_LOCALE, newLocale);
|
| + editor.apply();
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * Honor the Android system setting about showing the last character of a password for a short
|
| + * period of time.
|
| + */
|
| + private void updatePasswordEchoState() {
|
| + boolean systemEnabled = Settings.System.getInt(mApplication.getContentResolver(),
|
| + Settings.System.TEXT_SHOW_PASSWORD, 1) == 1;
|
| + if (PrefServiceBridge.getInstance().getPasswordEchoEnabled() == systemEnabled) return;
|
| +
|
| + PrefServiceBridge.getInstance().setPasswordEchoEnabled(systemEnabled);
|
| + }
|
| +
|
| + /**
|
| + * Caches flags that are needed by Activities that launch before the native library is loaded
|
| + * and stores them in SharedPreferences. Because this function is called during launch after the
|
| + * library has loaded, they won't affect the next launch until Chrome is restarted.
|
| + */
|
| + private void cacheNativeFlags() {
|
| + if (mIsFinishedCachingNativeFlags) return;
|
| + FeatureUtilities.cacheNativeFlags(mApplication);
|
| + mIsFinishedCachingNativeFlags = true;
|
| + }
|
| +
|
| + /**
|
| + * @return The PowerBroadcastReceiver for the browser process.
|
| + */
|
| + @VisibleForTesting
|
| + public PowerBroadcastReceiver getPowerBroadcastReceiverForTesting() {
|
| + return mPowerBroadcastReceiver;
|
| + }
|
| +}
|
|
|