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

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java

Issue 2348583002: Split out foreground session tracking from the application code. (Closed)
Patch Set: fix test Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/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;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698