Chromium Code Reviews| 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..300a60cb510eac54d126ca6753b50d2861924eb4 100644 |
| --- a/base/android/java/src/org/chromium/base/ActivityStatus.java |
| +++ b/base/android/java/src/org/chromium/base/ActivityStatus.java |
| @@ -7,6 +7,7 @@ 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; |
| @@ -29,24 +30,88 @@ public class ActivityStatus { |
| public static final int STOPPED = ActivityState.STOPPED; |
| public static final int DESTROYED = ActivityState.DESTROYED; |
| - // Last activity that was shown (or null if none or it was destroyed). |
| + public static final int HAS_RUNNING_ACTIVITIES = ApplicationState.HAS_RUNNING_ACTIVITIES; |
| + public static final int HAS_ONLY_PAUSED_ACTIVITIES = |
| + ApplicationState.HAS_ONLY_PAUSED_ACTIVITIES; |
| + public static final int HAS_ONLY_STOPPED_ACTIVITIES = |
| + ApplicationState.HAS_ONLY_STOPPED_ACTIVITIES; |
| + public static final int HAS_ONLY_DESTROYED_ACTIVITIES = |
| + ApplicationState.HAS_ONLY_DESTROYED_ACTIVITIES; |
|
bulach
2014/02/17 12:04:01
are these needed? can't we use ApplicationState di
David Trainor- moved to gerrit
2014/02/18 19:54:15
Done.
|
| + |
| + private static class ActivityInfo { |
| + private int mStatus = DESTROYED; |
| + private ObserverList<ActivityStateListener> mListeners = |
| + new ObserverList<ActivityStateListener>(); |
| + |
| + /** |
| + * @return The current {@link ActivityState} of the activity. |
| + */ |
| + public int getStatus() { |
| + return mStatus; |
| + } |
| + |
| + /** |
| + * @param status The new {@link ActivityState} of the activity. |
| + */ |
| + public void setStatus(int status) { |
| + mStatus = status; |
| + } |
| + |
| + /** |
| + * @return A list of {@link ActivityStateListener}s listening to this activity. |
| + */ |
| + public ObserverList<ActivityStateListener> getListeners() { |
| + return mListeners; |
| + } |
| + } |
| + |
| + private static Application sApplication; |
| + |
| + private static Integer sCachedApplicationState; |
| + |
| + /** Last activity that was shown (or null if none or it was destroyed). */ |
| private static Activity sActivity; |
| - private static final Map<Activity, Integer> sActivityStates = |
| - new HashMap<Activity, Integer>(); |
| + /** |
| + * |
| + */ |
| + private static final Map<Activity, ActivityInfo> sActivityInfo = |
| + new HashMap<Activity, ActivityInfo>(); |
| + |
| + /** |
| + * |
| + */ |
| + private static final ObserverList<ActivityStateListener> sGeneralActivityStateListeners = |
| + new 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>(); |
| - private static final ObserverList<StateListener> sStateListeners = |
| - new ObserverList<StateListener>(); |
| + /** |
| + * Interface to be implemented by listeners. |
| + */ |
| + 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 StateListener { |
| + 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 +121,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 +185,46 @@ 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(); |
| + |
| + if (newState == CREATED) { |
| + assert !sActivityInfo.containsKey(activity); |
| + sActivityInfo.put(activity, new ActivityInfo()); |
| } |
| - if (sActivity == activity) { |
| - for (StateListener listener : sStateListeners) { |
| - listener.onActivityStateChange(newState); |
| - } |
| - if (newState == DESTROYED) { |
| - sActivity = null; |
| + // Invalidate the cached application state. |
| + sCachedApplicationState = null; |
| + |
| + ActivityInfo info = sActivityInfo.get(activity); |
| + info.setStatus(newState); |
| + |
| + // Notify all state observers that are specifically listening to this activity. |
| + for (ActivityStateListener listener : info.getListeners()) { |
| + listener.onActivityStateChange(activity, newState); |
| + } |
| + |
| + // Notify all state observers that are listening globally for all activity state |
| + // changes. |
| + for (ActivityStateListener listener : sGeneralActivityStateListeners) { |
| + listener.onActivityStateChange(activity, newState); |
| + } |
| + |
| + int applicationState = getStateForApplication(); |
| + if (applicationState != oldApplicationState) { |
| + for (ApplicationStateListener listener : sApplicationStateListeners) { |
| + listener.onApplicationStateChange(applicationState); |
| } |
| } |
| + |
| + if (newState == DESTROYED) { |
| + sActivityInfo.remove(activity); |
| + if (activity == sActivity) sActivity = null; |
| + } |
| } |
| /** |
| @@ -138,18 +235,18 @@ public class ActivityStatus { |
| } |
| /** |
| - * @return The current activity. |
| + * @return The most recent focused {@link Activity} tracked by this class. Being focused means |
| + * out of all the activities tracked here, it has most recently gained window focus. |
| */ |
| - public static Activity getActivity() { |
| + public static Activity getLastTrackedFocusedActivity() { |
| 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,27 +286,93 @@ 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); |
| - return currentStatus != null ? currentStatus.intValue() : DESTROYED; |
| + ActivityInfo info = sActivityInfo.get(activity); |
| + return info != null ? info.getStatus() : DESTROYED; |
| } |
| /** |
| - * Registers the given listener to receive activity state changes. |
| + * @return The state of the application (see {@link ApplicationState}). |
| + */ |
| + public static int getStateForApplication() { |
| + if (sCachedApplicationState == null) sCachedApplicationState = determineApplicationState(); |
| + |
| + return sCachedApplicationState.intValue(); |
| + } |
| + |
| + /** |
| + * 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 Whether any Activity under this Application is visible. |
| + */ |
| + public static boolean hasVisibleActivities() { |
| + int state = getStateForApplication(); |
| + return state == HAS_RUNNING_ACTIVITIES || state == HAS_ONLY_PAUSED_ACTIVITIES; |
| + } |
| + |
| + /** |
| + * Checks to see if there are any active Activity instances being watched by ActivityStatus. |
| + * @return True if all Activities have been destroyed. |
| + */ |
| + public static boolean isEveryActivityDestroyed() { |
| + return sActivityInfo.isEmpty(); |
| + } |
| + |
| + /** |
| + * Registers the given listener to receive state changes for all activities. |
| * @param listener Listener to receive state changes. |
| */ |
| - public static void registerStateListener(StateListener listener) { |
| - sStateListeners.addObserver(listener); |
| + public static void registerStateListenerForAllActivities(ActivityStateListener listener) { |
| + sGeneralActivityStateListeners.addObserver(listener); |
| + } |
| + |
| + /** |
| + * 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 registerStateListenerForActivity(ActivityStateListener listener, |
| + Activity activity) { |
| + assert activity != null; |
| + |
| + ActivityInfo info = sActivityInfo.get(activity); |
| + assert info != null && info.getStatus() != DESTROYED; |
| + info.getListeners().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) { |
| + sGeneralActivityStateListeners.removeObserver(listener); |
| + |
| + // Loop through all observer lists for all activities and remove the listener. |
| + for (ActivityInfo info : sActivityInfo.values()) { |
| + info.getListeners().removeObserver(listener); |
| + } |
| + } |
| + |
| + /** |
| + * 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,41 +382,50 @@ 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); |
| } |
| }); |
| } |
| }); |
| } |
| - // 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); |
| - |
| /** |
| - * 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. |
| + * Determines the current application state as defined by {@link ApplicationState}. This will |
| + * loop over all the activities and check their state to determine what the general application |
| + * state should be. |
| + * @return {@link #HAS_RUNNING_ACTIVITIES} if any activity is not paused, stopped, or destroyed. |
| + * {@link #HAS_ONLY_PAUSED_ACTIVITIES} if none are running and one is paused. |
| + * {@link #HAS_ONLY_STOPPED_ACTIVITIES} if none are running/paused and one is stopped. |
| + * {@link #HAS_ONLY_DESTROYED_ACTIVITIES} if none are running/paused/stopped. |
| */ |
| - public static boolean isApplicationVisible() { |
| - int state = getState(); |
| - return state != STOPPED && state != DESTROYED; |
| - } |
| + private static int determineApplicationState() { |
| + boolean hasPausedActivity = false; |
| + boolean hasStoppedActivity = false; |
| - /** |
| - * Checks to see if there are any active Activity instances being watched by ActivityStatus. |
| - * @return True if all Activities have been destroyed. |
| - */ |
| - public static boolean isEveryActivityDestroyed() { |
| - return sActivityStates.isEmpty(); |
| + for (ActivityInfo info : sActivityInfo.values()) { |
| + int state = info.getStatus(); |
| + if (state != PAUSED && state != STOPPED && state != DESTROYED) { |
| + return HAS_RUNNING_ACTIVITIES; |
| + } else if (state == PAUSED) { |
| + hasPausedActivity = true; |
| + } else if (state == STOPPED) { |
| + hasStoppedActivity = true; |
| + } |
| + } |
| + |
| + if (hasPausedActivity) return HAS_ONLY_PAUSED_ACTIVITIES; |
| + if (hasStoppedActivity) return HAS_ONLY_STOPPED_ACTIVITIES; |
| + return HAS_ONLY_DESTROYED_ACTIVITIES; |
| } |
| + |
| + // Called to notify the native side of state changes. |
| + // IMPORTANT: This is always called on the main thread! |
| + private static native void nativeOnApplicationStateChange(int newState); |
| } |