1 // Copyright (c) 2012 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.
5 package org.chromium.base;
7 import android.app.Activity;
8 import android.app.Application;
9 import android.app.Application.ActivityLifecycleCallbacks;
10 import android.os.Bundle;
12 import java.util.HashMap;
16 * Provides information about the current activity's status, and a way
17 * to register / unregister listeners for state changes.
19 @JNINamespace("base::android")
20 public class ActivityStatus {
22 // Constants matching activity states reported to StateListener.onStateChange
23 // As an implementation detail, these are now defined in the auto-generated
24 // ActivityState interface, to be shared with C++.
25 public static final int CREATED = ActivityState.CREATED;
26 public static final int STARTED = ActivityState.STARTED;
27 public static final int RESUMED = ActivityState.RESUMED;
28 public static final int PAUSED = ActivityState.PAUSED;
29 public static final int STOPPED = ActivityState.STOPPED;
30 public static final int DESTROYED = ActivityState.DESTROYED;
32 // Last activity that was shown (or null if none or it was destroyed).
33 private static Activity sActivity;
35 private static final Map<Activity, Integer> sActivityStates
36 = new HashMap<Activity, Integer>();
38 private static final ObserverList<StateListener> sStateListeners
39 = new ObserverList<StateListener>();
42 * Interface to be implemented by listeners.
44 public interface StateListener {
46 * Called when the activity's state changes.
47 * @param newState New activity state.
49 public void onActivityStateChange(int newState);
52 private ActivityStatus() {}
55 * Initializes the activity status for a specified application.
57 * @param application The application whose status you wish to monitor.
59 public static void initialize(Application application) {
60 application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
62 public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
63 onStateChange(activity, CREATED);
67 public void onActivityDestroyed(Activity activity) {
68 onStateChange(activity, DESTROYED);
72 public void onActivityPaused(Activity activity) {
73 onStateChange(activity, PAUSED);
77 public void onActivityResumed(Activity activity) {
78 onStateChange(activity, RESUMED);
82 public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
85 public void onActivityStarted(Activity activity) {
86 onStateChange(activity, STARTED);
90 public void onActivityStopped(Activity activity) {
91 onStateChange(activity, STOPPED);
97 * Must be called by the main activity when it changes state.
99 * @param activity Current activity.
100 * @param newState New state value.
102 private static void onStateChange(Activity activity, int newState) {
103 if (activity == null) throw new IllegalArgumentException("null activity is not supported");
105 if (sActivity != activity) {
106 // ActivityStatus is notified with the CREATED event very late during the main activity
107 // creation to avoid making startup performance worse than it is by notifying observers
108 // that could do some expensive work. This can lead to non-CREATED events being fired
109 // before the CREATED event which is problematic.
110 // TODO(pliard): fix http://crbug.com/176837.
111 if (sActivity == null
112 || newState == CREATED || newState == RESUMED || newState == STARTED) {
113 sActivity = activity;
117 if (newState != DESTROYED) {
118 sActivityStates.put(activity, newState);
120 sActivityStates.remove(activity);
123 if (sActivity == activity) {
124 for (StateListener listener : sStateListeners) {
125 listener.onActivityStateChange(newState);
127 if (newState == DESTROYED) {
134 * Testing method to update the state of the specified activity.
136 public static void onStateChangeForTesting(Activity activity, int newState) {
137 onStateChange(activity, newState);
141 * @return The current activity.
143 public static Activity getActivity() {
148 * @return The current activity's state (if no activity is registered, then DESTROYED will
151 public static int getState() {
152 return getStateForActivity(sActivity);
156 * Query the state for a given activity. If the activity is not being tracked, this will
157 * return {@link #DESTROYED}.
160 * When relying on this method, be familiar with the expected life cycle state
162 * <a href="http://developer.android.com/guide/components/activities.html#Lifecycle">
167 * During activity transitions (activity B launching in front of activity A), A will completely
168 * paused before the creation of activity B begins.
171 * A basic flow for activity A starting, followed by activity B being opened and then closed:
173 * <li> -- Starting Activity A --
174 * <li> Activity A - CREATED
175 * <li> Activity A - STARTED
176 * <li> Activity A - RESUMED
177 * <li> -- Starting Activity B --
178 * <li> Activity A - PAUSED
179 * <li> Activity B - CREATED
180 * <li> Activity B - STARTED
181 * <li> Activity B - RESUMED
182 * <li> Activity A - STOPPED
183 * <li> -- Closing Activity B, Activity A regaining focus --
184 * <li> Activity B - PAUSED
185 * <li> Activity A - STARTED
186 * <li> Activity A - RESUMED
187 * <li> Activity B - STOPPED
188 * <li> Activity B - DESTROYED
191 * @param activity The activity whose state is to be returned.
192 * @return The state of the specified activity.
194 public static int getStateForActivity(Activity activity) {
195 Integer currentStatus = sActivityStates.get(activity);
196 return currentStatus != null ? currentStatus.intValue() : DESTROYED;
200 * Registers the given listener to receive activity state changes.
201 * @param listener Listener to receive state changes.
203 public static void registerStateListener(StateListener listener) {
204 sStateListeners.addObserver(listener);
208 * Unregisters the given listener from receiving activity state changes.
209 * @param listener Listener that doesn't want to receive state changes.
211 public static void unregisterStateListener(StateListener listener) {
212 sStateListeners.removeObserver(listener);
216 * Registers the single thread-safe native activity status listener.
217 * This handles the case where the caller is not on the main thread.
218 * Note that this is used by a leaky singleton object from the native
219 * side, hence lifecycle management is greatly simplified.
222 private static void registerThreadSafeNativeStateListener() {
223 ThreadUtils.runOnUiThread(new Runnable () {
226 // Register a new listener that calls nativeOnActivityStateChange.
227 sStateListeners.addObserver(new StateListener() {
229 public void onActivityStateChange(int newState) {
230 nativeOnActivityStateChange(newState);
237 // Called to notify the native side of state changes.
238 // IMPORTANT: This is always called on the main thread!
239 private static native void nativeOnActivityStateChange(int newState);
242 * Checks whether or not the Application's current Activity is visible to the user. Note that
243 * this includes the PAUSED state, which can happen when the Activity is temporarily covered
244 * by another Activity's Fragment (e.g.).
245 * @return True if the Activity is visible, false otherwise.
247 public static boolean isApplicationVisible() {
248 int state = getState();
249 return state != STOPPED && state != DESTROYED;