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

Unified Diff: base/android/java/src/org/chromium/base/ActivityStatus.java

Issue 159173002: Refactor ActivityStatus to not store current activity (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed Comments Created 6 years, 10 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: base/android/java/src/org/chromium/base/ActivityStatus.java
diff --git a/base/android/java/src/org/chromium/base/ActivityStatus.java b/base/android/java/src/org/chromium/base/ActivityStatus.java
index df43dbebfbb9bf95df55fbb72607ecd9a1f85ff2..93144bba6b12b0a2af91de3a93d4bd2297235782 100644
--- a/base/android/java/src/org/chromium/base/ActivityStatus.java
+++ b/base/android/java/src/org/chromium/base/ActivityStatus.java
@@ -7,9 +7,11 @@ package org.chromium.base;
import android.app.Activity;
import android.app.Application;
import android.app.Application.ActivityLifecycleCallbacks;
+import android.content.Context;
import android.os.Bundle;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
/**
@@ -29,24 +31,59 @@ public class ActivityStatus {
public static final int STOPPED = ActivityState.STOPPED;
public static final int DESTROYED = ActivityState.DESTROYED;
+ public static final int APP_RUNNING = ApplicationState.RUNNING;
+ public static final int APP_PAUSED = ApplicationState.PAUSED;
+ public static final int APP_STOPPED = ApplicationState.STOPPED;
+ public static final int APP_DESTROYED = ApplicationState.DESTROYED;
+
+ private static Application sApplication;
+
// Last activity that was shown (or null if none or it was destroyed).
+ // TODO(dtrainor): Add window focus listener to update this.
Ted C 2014/02/13 04:38:44 aren't you doing this down below?
David Trainor- moved to gerrit 2014/02/14 19:13:36 Done.
private static Activity sActivity;
+ /**
+ * A map of {@link Activity} to {@link ActivityState}. If an {@link Activity} is not present it
+ * can be assumed that it was destroyed.
+ */
private static final Map<Activity, Integer> sActivityStates =
Ted C 2014/02/13 04:38:44 Hmm...I wonder if we should combine this and the s
David Trainor- moved to gerrit 2014/02/13 05:03:55 No it's a good idea. I thought about it but forgo
new HashMap<Activity, Integer>();
- private static final ObserverList<StateListener> sStateListeners =
- new ObserverList<StateListener>();
+ /**
+ * A set of observers for each {@link Activity}. Observers on the {@code null} key in this map
+ * will observer all changes for all {@link Activity}s.
+ */
+ private static final Map<Activity, ObserverList<ActivityStateListener>>
+ sActivityStateListeners = new HashMap<Activity, ObserverList<ActivityStateListener>>();
+
+ /**
+ * A list of observers to be notified when the visibility state of this {@link Application}
+ * changes. See {@link #getStateForApplication()}.
+ */
+ private static final ObserverList<ApplicationStateListener> sApplicationStateListeners =
+ new ObserverList<ApplicationStateListener>();
/**
* Interface to be implemented by listeners.
*/
- public interface StateListener {
+ public interface ApplicationStateListener {
+ /**
+ * Called when the application's state changes.
+ * @param newState The application state.
+ */
+ public void onApplicationStateChange(int newState);
+ }
+
+ /**
+ * Interface to be implemented by listeners.
+ */
+ public interface ActivityStateListener {
/**
* Called when the activity's state changes.
+ * @param activity The activity that had a state change.
* @param newState New activity state.
*/
- public void onActivityStateChange(int newState);
+ public void onActivityStateChange(Activity activity, int newState);
}
private ActivityStatus() {}
@@ -56,10 +93,28 @@ public class ActivityStatus {
*
* @param application The application whose status you wish to monitor.
*/
- public static void initialize(Application application) {
+ public static void initialize(BaseChromiumApplication application) {
+ sApplication = application;
+
+ application.registerWindowFocusChangedListener(
+ new BaseChromiumApplication.WindowFocusChangedListener() {
+ @Override
+ public void onWindowFocusChanged(Activity activity, boolean hasFocus) {
+ if (!hasFocus || activity == sActivity) return;
+
+ int state = getStateForActivity(activity);
+
+ if (state != DESTROYED && state != STOPPED) {
+ sActivity = activity;
+ }
+
+ // TODO(dtrainor): Notify of active activity change?
+ }
+ });
+
application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
- public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ public void onActivityCreated(final Activity activity, Bundle savedInstanceState) {
onStateChange(activity, CREATED);
}
@@ -102,32 +157,44 @@ public class ActivityStatus {
private static void onStateChange(Activity activity, int newState) {
if (activity == null) throw new IllegalArgumentException("null activity is not supported");
- if (sActivity != activity) {
- // ActivityStatus is notified with the CREATED event very late during the main activity
- // creation to avoid making startup performance worse than it is by notifying observers
- // that could do some expensive work. This can lead to non-CREATED events being fired
- // before the CREATED event which is problematic.
- // TODO(pliard): fix http://crbug.com/176837.
- if (sActivity == null
- || newState == CREATED || newState == RESUMED || newState == STARTED) {
- sActivity = activity;
- }
+ if (sActivity == null
+ || newState == CREATED || newState == RESUMED || newState == STARTED) {
+ sActivity = activity;
}
- if (newState != DESTROYED) {
- sActivityStates.put(activity, newState);
- } else {
- sActivityStates.remove(activity);
+ int oldApplicationState = getStateForApplication();
+ sActivityStates.put(activity, newState);
+
+ // Notify all state observers that are specifically listening to this activity.
+ ObserverList<ActivityStateListener> listeners = sActivityStateListeners.get(activity);
+ if (listeners != null) {
+ for (ActivityStateListener listener : listeners) {
+ listener.onActivityStateChange(activity, newState);
+ }
}
- if (sActivity == activity) {
- for (StateListener listener : sStateListeners) {
- listener.onActivityStateChange(newState);
+ // Notify all state observers that are listening globally for all activity state
+ // changes.
+ ObserverList<ActivityStateListener> globalListeners = sActivityStateListeners.get(null);
+ if (globalListeners != null) {
+ for (ActivityStateListener listener : globalListeners) {
+ listener.onActivityStateChange(activity, newState);
}
- if (newState == DESTROYED) {
- sActivity = null;
+ }
+
+ int applicationState = getStateForApplication();
+ if (applicationState != oldApplicationState) {
+ for (ApplicationStateListener listener : sApplicationStateListeners) {
+ listener.onApplicationStateChange(applicationState);
}
}
+
+ if (newState == DESTROYED) {
+ sActivityStateListeners.remove(activity);
+ sActivityStates.remove(activity);
+
+ if (activity == sActivity) sActivity = null;
+ }
}
/**
@@ -138,18 +205,18 @@ public class ActivityStatus {
}
/**
- * @return The current activity.
+ * @return The first visible {@link Activity} tracked by this class. Being visible is defined
bulach 2014/02/13 11:17:48 hmm.. I wish we could call this "focused", but I s
David Trainor- moved to gerrit 2014/02/14 19:13:36 Yeah I used to return a list from this. But I fou
+ * as being in a state of created, started, or resumed.
Ted C 2014/02/13 04:38:44 indent to be aligned with "The" above Also since
David Trainor- moved to gerrit 2014/02/14 19:13:36 I updated the comment. I think I understand your
*/
- public static Activity getActivity() {
+ public static Activity getVisibleActivity() {
return sActivity;
}
/**
- * @return The current activity's state (if no activity is registered, then DESTROYED will
- * be returned).
+ * @return The {@link Context} for the {@link Application}.
*/
- public static int getState() {
- return getStateForActivity(sActivity);
+ public static Context getApplicationContext() {
+ return sApplication != null ? sApplication.getApplicationContext() : null;
}
/**
@@ -189,7 +256,7 @@ public class ActivityStatus {
* </ul>
*
* @param activity The activity whose state is to be returned.
- * @return The state of the specified activity.
+ * @return The state of the specified activity (see {@link ActivityState}).
*/
public static int getStateForActivity(Activity activity) {
Integer currentStatus = sActivityStates.get(activity);
@@ -197,19 +264,92 @@ public class ActivityStatus {
}
/**
- * Registers the given listener to receive activity state changes.
+ * @return The state of the application (see {@link ApplicationState}).
+ */
+ public static int getStateForApplication() {
bulach 2014/02/13 11:17:48 hmm, I'm not sure, but I think it's bit unnatural
David Trainor- moved to gerrit 2014/02/14 19:13:36 Sounds reasonable. I changed the states to match
+ boolean running = false;
+ boolean paused = false;
+ boolean stopped = false;
+
+ for (Integer state : sActivityStates.values()) {
+ running |= state != PAUSED && state != STOPPED && state != DESTROYED;
Ted C 2014/02/13 04:38:44 Seems like you can drop running and just return AP
David Trainor- moved to gerrit 2014/02/14 19:13:36 Done.
+ paused |= state != STOPPED && state != DESTROYED;
+ stopped |= state != DESTROYED;
+ }
+
+ if (running) return APP_RUNNING;
+ if (paused) return APP_PAUSED;
+ if (stopped) return APP_STOPPED;
+ return APP_DESTROYED;
+ }
+
+ /**
+ * Checks whether or not any Activity in this Application is visible to the user. Note that
+ * this includes the PAUSED state, which can happen when the Activity is temporarily covered
+ * by another Activity's Fragment (e.g.).
+ * @return True if the any Activity under this Application is visible, false otherwise.
Ted C 2014/02/13 04:38:44 Whether any Activity .... Then drop the "false ot
David Trainor- moved to gerrit 2014/02/14 19:13:36 Done.
+ */
+ public static boolean isApplicationVisible() {
bulach 2014/02/13 11:17:48 how about "hasAnyVisibleActivities" ?
David Trainor- moved to gerrit 2014/02/14 19:13:36 Done.
+ int state = getStateForApplication();
+ return state == APP_RUNNING || state == APP_PAUSED;
+ }
+
+ /**
+ * Registers the given listener to receive state changes for all activities.
+ * @param listener Listener to receive state changes.
+ */
+ public static void registerStateListenerForAllActivities(ActivityStateListener listener) {
Ted C 2014/02/13 04:38:44 Does anyone actually need to listen for all activi
David Trainor- moved to gerrit 2014/02/13 05:03:55 There were one or two consumers as of when I moved
+ registerStateListenerForActivity(listener, null);
+ }
+
+ /**
+ * Registers the given listener to receive state changes for {@code activity}. After a call to
+ * {@link ActivityStateListener#onActivityStateChange(Activity, int)} with
+ * {@link ActivityState#DESTROYED} all listeners associated with that particular
+ * {@link Activity} are removed.
* @param listener Listener to receive state changes.
+ * @param activity Activity to track or {@code null} to track all activities.
*/
- public static void registerStateListener(StateListener listener) {
- sStateListeners.addObserver(listener);
+ public static void registerStateListenerForActivity(ActivityStateListener listener,
+ Activity activity) {
+ assert getStateForActivity(activity) != DESTROYED;
+ ObserverList<ActivityStateListener> list = sActivityStateListeners.get(activity);
+ if (list == null) {
Ted C 2014/02/13 04:38:44 Personally, I would change this to always initiali
David Trainor- moved to gerrit 2014/02/14 19:13:36 Done.
+ list = new ObserverList<ActivityStateListener>();
+ sActivityStateListeners.put(activity, list);
+ }
+ list.addObserver(listener);
}
/**
* Unregisters the given listener from receiving activity state changes.
* @param listener Listener that doesn't want to receive state changes.
*/
- public static void unregisterStateListener(StateListener listener) {
- sStateListeners.removeObserver(listener);
+ public static void unregisterActivityStateListener(ActivityStateListener listener) {
+ // Loop through all observer lists for all activities and remove the listener. If the
+ // list is empty after removing the listener remove the activity entry as well.
+ for (Iterator<Map.Entry<Activity, ObserverList<ActivityStateListener>>> it =
+ sActivityStateListeners.entrySet().iterator(); it.hasNext();) {
+ Map.Entry<Activity, ObserverList<ActivityStateListener>> entry = it.next();
+ entry.getValue().removeObserver(listener);
+ if (!entry.getValue().iterator().hasNext()) it.remove();
+ }
+ }
+
+ /**
+ * Registers the given listener to receive state changes for the application.
+ * @param listener Listener to receive state state changes.
+ */
+ public static void registerApplicationStateListener(ApplicationStateListener listener) {
+ sApplicationStateListeners.addObserver(listener);
+ }
+
+ /**
+ * Unregisters the given listener from receiving state changes.
+ * @param listener Listener that doesn't want to receive state changes.
+ */
+ public static void unregisterApplicationStateListener(ApplicationStateListener listener) {
+ sApplicationStateListeners.removeObserver(listener);
}
/**
@@ -219,15 +359,14 @@ public class ActivityStatus {
* side, hence lifecycle management is greatly simplified.
*/
@CalledByNative
- private static void registerThreadSafeNativeStateListener() {
+ private static void registerThreadSafeNativeApplicationStateListener() {
ThreadUtils.runOnUiThread(new Runnable () {
@Override
public void run() {
- // Register a new listener that calls nativeOnActivityStateChange.
- sStateListeners.addObserver(new StateListener() {
+ registerApplicationStateListener(new ApplicationStateListener() {
@Override
- public void onActivityStateChange(int newState) {
- nativeOnActivityStateChange(newState);
+ public void onApplicationStateChange(int newState) {
+ nativeOnApplicationStateChange(newState);
}
});
}
@@ -236,17 +375,18 @@ public class ActivityStatus {
// Called to notify the native side of state changes.
// IMPORTANT: This is always called on the main thread!
- private static native void nativeOnActivityStateChange(int newState);
+ private static native void nativeOnApplicationStateChange(int newState);
/**
- * Checks whether or not the Application's current Activity is visible to the user. Note that
- * this includes the PAUSED state, which can happen when the Activity is temporarily covered
- * by another Activity's Fragment (e.g.).
- * @return True if the Activity is visible, false otherwise.
+ * Checks wehther or not the Application is active. This means Activity#onStart() has been
+ * called but Activity#onStop() has not.
+ * @return Whether or not the application is active.
*/
- public static boolean isApplicationVisible() {
- int state = getState();
- return state != STOPPED && state != DESTROYED;
+ public static boolean isApplicationActive() {
Ted C 2014/02/13 04:38:44 Active is still an odd sounding name. Maybe Foreg
David Trainor- moved to gerrit 2014/02/13 05:03:55 Oops this might be an old function I can erase...
David Trainor- moved to gerrit 2014/02/14 19:13:36 Done.
+ for (Integer state : sActivityStates.values()) {
+ if (state == RESUMED || state == STARTED) return true;
Ted C 2014/02/13 04:38:44 can you not use the application state here? Also,
David Trainor- moved to gerrit 2014/02/14 19:13:36 Done.
+ }
+ return false;
}
/**

Powered by Google App Engine
This is Rietveld 408576698