- add sources.
[platform/framework/web/crosswalk.git] / src / base / android / java / src / org / chromium / base / ActivityStatus.java
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.
4
5 package org.chromium.base;
6
7 import android.app.Activity;
8 import android.app.Application;
9 import android.app.Application.ActivityLifecycleCallbacks;
10 import android.os.Bundle;
11
12 import java.util.HashMap;
13 import java.util.Map;
14
15 /**
16  * Provides information about the current activity's status, and a way
17  * to register / unregister listeners for state changes.
18  */
19 @JNINamespace("base::android")
20 public class ActivityStatus {
21
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;
31
32     // Last activity that was shown (or null if none or it was destroyed).
33     private static Activity sActivity;
34
35     private static final Map<Activity, Integer> sActivityStates
36             = new HashMap<Activity, Integer>();
37
38     private static final ObserverList<StateListener> sStateListeners
39             = new ObserverList<StateListener>();
40
41     /**
42      * Interface to be implemented by listeners.
43      */
44     public interface StateListener {
45         /**
46          * Called when the activity's state changes.
47          * @param newState New activity state.
48          */
49         public void onActivityStateChange(int newState);
50     }
51
52     private ActivityStatus() {}
53
54     /**
55      * Initializes the activity status for a specified application.
56      *
57      * @param application The application whose status you wish to monitor.
58      */
59     public static void initialize(Application application) {
60         application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
61             @Override
62             public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
63                 onStateChange(activity, CREATED);
64             }
65
66             @Override
67             public void onActivityDestroyed(Activity activity) {
68                 onStateChange(activity, DESTROYED);
69             }
70
71             @Override
72             public void onActivityPaused(Activity activity) {
73                 onStateChange(activity, PAUSED);
74             }
75
76             @Override
77             public void onActivityResumed(Activity activity) {
78                 onStateChange(activity, RESUMED);
79             }
80
81             @Override
82             public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
83
84             @Override
85             public void onActivityStarted(Activity activity) {
86                 onStateChange(activity, STARTED);
87             }
88
89             @Override
90             public void onActivityStopped(Activity activity) {
91                 onStateChange(activity, STOPPED);
92             }
93         });
94     }
95
96     /**
97      * Must be called by the main activity when it changes state.
98      *
99      * @param activity Current activity.
100      * @param newState New state value.
101      */
102     private static void onStateChange(Activity activity, int newState) {
103         if (activity == null) throw new IllegalArgumentException("null activity is not supported");
104
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;
114             }
115         }
116
117         if (newState != DESTROYED) {
118             sActivityStates.put(activity, newState);
119         } else {
120             sActivityStates.remove(activity);
121         }
122
123         if (sActivity == activity) {
124             for (StateListener listener : sStateListeners) {
125                 listener.onActivityStateChange(newState);
126             }
127             if (newState == DESTROYED) {
128                 sActivity = null;
129             }
130         }
131     }
132
133     /**
134      * Testing method to update the state of the specified activity.
135      */
136     public static void onStateChangeForTesting(Activity activity, int newState) {
137         onStateChange(activity, newState);
138     }
139
140     /**
141      * @return The current activity.
142      */
143     public static Activity getActivity() {
144         return sActivity;
145     }
146
147     /**
148      * @return The current activity's state (if no activity is registered, then DESTROYED will
149      *         be returned).
150      */
151     public static int getState() {
152         return getStateForActivity(sActivity);
153     }
154
155     /**
156      * Query the state for a given activity.  If the activity is not being tracked, this will
157      * return {@link #DESTROYED}.
158      *
159      * <p>
160      * When relying on this method, be familiar with the expected life cycle state
161      * transitions:
162      * <a href="http://developer.android.com/guide/components/activities.html#Lifecycle">
163      *   Activity Lifecycle
164      * </a>
165      *
166      * <p>
167      * During activity transitions (activity B launching in front of activity A), A will completely
168      * paused before the creation of activity B begins.
169      *
170      * <p>
171      * A basic flow for activity A starting, followed by activity B being opened and then closed:
172      * <ul>
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
189      * </ul>
190      *
191      * @param activity The activity whose state is to be returned.
192      * @return The state of the specified activity.
193      */
194     public static int getStateForActivity(Activity activity) {
195         Integer currentStatus = sActivityStates.get(activity);
196         return currentStatus != null ? currentStatus.intValue() : DESTROYED;
197     }
198
199     /**
200      * Registers the given listener to receive activity state changes.
201      * @param listener Listener to receive state changes.
202      */
203     public static void registerStateListener(StateListener listener) {
204         sStateListeners.addObserver(listener);
205     }
206
207     /**
208      * Unregisters the given listener from receiving activity state changes.
209      * @param listener Listener that doesn't want to receive state changes.
210      */
211     public static void unregisterStateListener(StateListener listener) {
212         sStateListeners.removeObserver(listener);
213     }
214
215     /**
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.
220      */
221     @CalledByNative
222     private static void registerThreadSafeNativeStateListener() {
223         ThreadUtils.runOnUiThread(new Runnable () {
224             @Override
225             public void run() {
226                 // Register a new listener that calls nativeOnActivityStateChange.
227                 sStateListeners.addObserver(new StateListener() {
228                     @Override
229                     public void onActivityStateChange(int newState) {
230                         nativeOnActivityStateChange(newState);
231                     }
232                 });
233             }
234         });
235     }
236
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);
240
241     /**
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.
246      */
247     public static boolean isApplicationVisible() {
248         int state = getState();
249         return state != STOPPED && state != DESTROYED;
250     }
251 }