1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// found in the LICENSE file.
4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)package org.chromium.base;
6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.app.Activity;
8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.app.Application;
9a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.app.Application.ActivityLifecycleCallbacks;
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.content.Context;
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.os.Bundle;
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import java.lang.ref.WeakReference;
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import java.util.ArrayList;
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import java.util.List;
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import java.util.Map;
1703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import java.util.concurrent.ConcurrentHashMap;
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)/**
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Provides information about the current activity's status, and a way
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * to register / unregister listeners for state changes.
22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)@JNINamespace("base::android")
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)public class ApplicationStatus {
25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static class ActivityInfo {
26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        private int mStatus = ActivityState.DESTROYED;
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        private ObserverList<ActivityStateListener> mListeners =
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                new ObserverList<ActivityStateListener>();
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        /**
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         * @return The current {@link ActivityState} of the activity.
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         */
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        public int getStatus() {
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            return mStatus;
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        /**
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         * @param status The new {@link ActivityState} of the activity.
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         */
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        public void setStatus(int status) {
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            mStatus = status;
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        /**
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         * @return A list of {@link ActivityStateListener}s listening to this activity.
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         */
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        public ObserverList<ActivityStateListener> getListeners() {
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            return mListeners;
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static Application sApplication;
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
5403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    private static Object sCachedApplicationStateLock = new Object();
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static Integer sCachedApplicationState;
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /** Last activity that was shown (or null if none or it was destroyed). */
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static Activity sActivity;
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /** A lazily initialized listener that forwards application state changes to native. */
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static ApplicationStateListener sNativeApplicationStateListener;
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * A map of which observers listen to state changes from which {@link Activity}.
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static final Map<Activity, ActivityInfo> sActivityInfo =
6703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            new ConcurrentHashMap<Activity, ActivityInfo>();
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * A list of observers to be notified when any {@link Activity} has a state change.
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static final ObserverList<ActivityStateListener> sGeneralActivityStateListeners =
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            new ObserverList<ActivityStateListener>();
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * A list of observers to be notified when the visibility state of this {@link Application}
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * changes.  See {@link #getStateForApplication()}.
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static final ObserverList<ApplicationStateListener> sApplicationStateListeners =
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            new ObserverList<ApplicationStateListener>();
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Interface to be implemented by listeners.
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public interface ApplicationStateListener {
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        /**
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         * Called when the application's state changes.
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         * @param newState The application state.
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         */
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        public void onApplicationStateChange(int newState);
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Interface to be implemented by listeners.
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public interface ActivityStateListener {
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        /**
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         * Called when the activity's state changes.
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         * @param activity The activity that had a state change.
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         * @param newState New activity state.
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         */
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        public void onActivityStateChange(Activity activity, int newState);
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private ApplicationStatus() {}
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Initializes the activity status for a specified application.
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @param application The application whose status you wish to monitor.
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static void initialize(BaseChromiumApplication application) {
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        sApplication = application;
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        application.registerWindowFocusChangedListener(
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                new BaseChromiumApplication.WindowFocusChangedListener() {
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            @Override
118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            public void onWindowFocusChanged(Activity activity, boolean hasFocus) {
119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                if (!hasFocus || activity == sActivity) return;
120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                int state = getStateForActivity(activity);
122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                if (state != ActivityState.DESTROYED && state != ActivityState.STOPPED) {
124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    sActivity = activity;
125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                }
126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                // TODO(dtrainor): Notify of active activity change?
128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            }
129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        });
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            @Override
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            public void onActivityCreated(final Activity activity, Bundle savedInstanceState) {
134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                onStateChange(activity, ActivityState.CREATED);
135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            }
136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            @Override
138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            public void onActivityDestroyed(Activity activity) {
139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                onStateChange(activity, ActivityState.DESTROYED);
140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            }
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            @Override
143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            public void onActivityPaused(Activity activity) {
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                onStateChange(activity, ActivityState.PAUSED);
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            }
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            @Override
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            public void onActivityResumed(Activity activity) {
149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                onStateChange(activity, ActivityState.RESUMED);
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            }
151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            @Override
153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            @Override
156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            public void onActivityStarted(Activity activity) {
157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                onStateChange(activity, ActivityState.STARTED);
158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            }
159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            @Override
161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            public void onActivityStopped(Activity activity) {
162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                onStateChange(activity, ActivityState.STOPPED);
163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            }
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        });
165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Must be called by the main activity when it changes state.
169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *
170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @param activity Current activity.
171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @param newState New state value.
172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static void onStateChange(Activity activity, int newState) {
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (activity == null) throw new IllegalArgumentException("null activity is not supported");
175a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
176a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (sActivity == null
177a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                || newState == ActivityState.CREATED
178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                || newState == ActivityState.RESUMED
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                || newState == ActivityState.STARTED) {
180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            sActivity = activity;
181a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
182a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        int oldApplicationState = getStateForApplication();
184a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (newState == ActivityState.CREATED) {
186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            assert !sActivityInfo.containsKey(activity);
187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            sActivityInfo.put(activity, new ActivityInfo());
188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // Invalidate the cached application state.
19103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        synchronized (sCachedApplicationStateLock) {
19203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            sCachedApplicationState = null;
19303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        }
194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        ActivityInfo info = sActivityInfo.get(activity);
196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        info.setStatus(newState);
197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // Notify all state observers that are specifically listening to this activity.
199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        for (ActivityStateListener listener : info.getListeners()) {
200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            listener.onActivityStateChange(activity, newState);
201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // Notify all state observers that are listening globally for all activity state
204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // changes.
205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        for (ActivityStateListener listener : sGeneralActivityStateListeners) {
206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            listener.onActivityStateChange(activity, newState);
207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        int applicationState = getStateForApplication();
210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (applicationState != oldApplicationState) {
211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            for (ApplicationStateListener listener : sApplicationStateListeners) {
212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                listener.onApplicationStateChange(applicationState);
213a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            }
214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (newState == ActivityState.DESTROYED) {
217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            sActivityInfo.remove(activity);
218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            if (activity == sActivity) sActivity = null;
219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
223a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Testing method to update the state of the specified activity.
224a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
2251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    @VisibleForTesting
226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static void onStateChangeForTesting(Activity activity, int newState) {
227a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        onStateChange(activity, newState);
228a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
229a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
230a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
231a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @return The most recent focused {@link Activity} tracked by this class.  Being focused means
232a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *         out of all the activities tracked here, it has most recently gained window focus.
233a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
234a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static Activity getLastTrackedFocusedActivity() {
235a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return sActivity;
236a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
237a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
238a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
239a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @return A {@link List} of all non-destroyed {@link Activity}s.
240a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
241a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static List<WeakReference<Activity>> getRunningActivities() {
242a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        List<WeakReference<Activity>> activities = new ArrayList<WeakReference<Activity>>();
243a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        for (Activity activity : sActivityInfo.keySet()) {
244a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            activities.add(new WeakReference<Activity>(activity));
245a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
246a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return activities;
247a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
249a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
250a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @return The {@link Context} for the {@link Application}.
251a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
252a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static Context getApplicationContext() {
253a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return sApplication != null ? sApplication.getApplicationContext() : null;
254a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
255a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
256a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
257a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Query the state for a given activity.  If the activity is not being tracked, this will
258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * return {@link ActivityState#DESTROYED}.
259a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *
260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * <p>
261a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Please note that Chrome can have multiple activities running simultaneously.  Please also
262a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * look at {@link #getStateForApplication()} for more details.
263a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *
264a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * <p>
265a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * When relying on this method, be familiar with the expected life cycle state
266a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * transitions:
267a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * <a href="http://developer.android.com/guide/components/activities.html#Lifecycle">
268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   Activity Lifecycle
269a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * </a>
270a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *
271a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * <p>
272a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * During activity transitions (activity B launching in front of activity A), A will completely
273a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * paused before the creation of activity B begins.
274a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *
275a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * <p>
276a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * A basic flow for activity A starting, followed by activity B being opened and then closed:
277a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * <ul>
278a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> -- Starting Activity A --
279a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity A - ActivityState.CREATED
280a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity A - ActivityState.STARTED
281a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity A - ActivityState.RESUMED
282a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> -- Starting Activity B --
283a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity A - ActivityState.PAUSED
284a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity B - ActivityState.CREATED
285a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity B - ActivityState.STARTED
286a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity B - ActivityState.RESUMED
287a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity A - ActivityState.STOPPED
288a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> -- Closing Activity B, Activity A regaining focus --
289a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity B - ActivityState.PAUSED
290a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity A - ActivityState.STARTED
291a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity A - ActivityState.RESUMED
292a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity B - ActivityState.STOPPED
293a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *   <li> Activity B - ActivityState.DESTROYED
294a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * </ul>
295a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *
296a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @param activity The activity whose state is to be returned.
297a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @return The state of the specified activity (see {@link ActivityState}).
298a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
299a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static int getStateForActivity(Activity activity) {
300a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        ActivityInfo info = sActivityInfo.get(activity);
301a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return info != null ? info.getStatus() : ActivityState.DESTROYED;
302a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
303a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
304a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
305a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @return The state of the application (see {@link ApplicationState}).
306a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
307a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static int getStateForApplication() {
30803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        synchronized (sCachedApplicationStateLock) {
30903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            if (sCachedApplicationState == null) {
31003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                sCachedApplicationState = determineApplicationState();
31103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            }
31203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        }
313a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
314a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return sCachedApplicationState.intValue();
315a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
316a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
317a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
318a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Checks whether or not any Activity in this Application is visible to the user.  Note that
319a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * this includes the PAUSED state, which can happen when the Activity is temporarily covered
320a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * by another Activity's Fragment (e.g.).
321a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @return Whether any Activity under this Application is visible.
322a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
323a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static boolean hasVisibleActivities() {
324a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        int state = getStateForApplication();
325a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return state == ApplicationState.HAS_RUNNING_ACTIVITIES
326a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                || state == ApplicationState.HAS_PAUSED_ACTIVITIES;
327a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
328a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
329a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
330a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Checks to see if there are any active Activity instances being watched by ApplicationStatus.
331a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @return True if all Activities have been destroyed.
332a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
333a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static boolean isEveryActivityDestroyed() {
334a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return sActivityInfo.isEmpty();
335a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
336a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
337a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
338a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Registers the given listener to receive state changes for all activities.
339a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @param listener Listener to receive state changes.
340a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
341a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static void registerStateListenerForAllActivities(ActivityStateListener listener) {
342a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        sGeneralActivityStateListeners.addObserver(listener);
343a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
344a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
345a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
346a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Registers the given listener to receive state changes for {@code activity}.  After a call to
347a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * {@link ActivityStateListener#onActivityStateChange(Activity, int)} with
348a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * {@link ActivityState#DESTROYED} all listeners associated with that particular
349a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * {@link Activity} are removed.
350a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @param listener Listener to receive state changes.
351a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @param activity Activity to track or {@code null} to track all activities.
352a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
353a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static void registerStateListenerForActivity(ActivityStateListener listener,
354a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            Activity activity) {
355a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        assert activity != null;
356a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
357a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        ActivityInfo info = sActivityInfo.get(activity);
358a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        assert info != null && info.getStatus() != ActivityState.DESTROYED;
359a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        info.getListeners().addObserver(listener);
360a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
361a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
362a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
363a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Unregisters the given listener from receiving activity state changes.
364a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @param listener Listener that doesn't want to receive state changes.
365a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
366a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static void unregisterActivityStateListener(ActivityStateListener listener) {
367a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        sGeneralActivityStateListeners.removeObserver(listener);
368a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
369a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // Loop through all observer lists for all activities and remove the listener.
370a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        for (ActivityInfo info : sActivityInfo.values()) {
371a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            info.getListeners().removeObserver(listener);
372a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
373a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
374a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
375a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
376a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Registers the given listener to receive state changes for the application.
377a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @param listener Listener to receive state state changes.
378a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
379a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static void registerApplicationStateListener(ApplicationStateListener listener) {
380a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        sApplicationStateListeners.addObserver(listener);
381a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
382a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
383a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
384a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Unregisters the given listener from receiving state changes.
385a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @param listener Listener that doesn't want to receive state changes.
386a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
387a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public static void unregisterApplicationStateListener(ApplicationStateListener listener) {
388a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        sApplicationStateListeners.removeObserver(listener);
389a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
390a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
391a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
392a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Registers the single thread-safe native activity status listener.
393a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * This handles the case where the caller is not on the main thread.
394a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Note that this is used by a leaky singleton object from the native
395a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * side, hence lifecycle management is greatly simplified.
396a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
397a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    @CalledByNative
398a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static void registerThreadSafeNativeApplicationStateListener() {
399a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        ThreadUtils.runOnUiThread(new Runnable () {
400a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            @Override
401a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            public void run() {
402a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                if (sNativeApplicationStateListener != null) return;
403a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
404a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                sNativeApplicationStateListener = new ApplicationStateListener() {
405a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    @Override
406a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    public void onApplicationStateChange(int newState) {
407a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                        nativeOnApplicationStateChange(newState);
408a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    }
409a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                };
410a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                registerApplicationStateListener(sNativeApplicationStateListener);
411a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            }
412a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        });
413a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
414a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
415a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
416a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * Determines the current application state as defined by {@link ApplicationState}.  This will
417a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * loop over all the activities and check their state to determine what the general application
418a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * state should be.
419a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @return HAS_RUNNING_ACTIVITIES if any activity is not paused, stopped, or destroyed.
420a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *         HAS_PAUSED_ACTIVITIES if none are running and one is paused.
421a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *         HAS_STOPPED_ACTIVITIES if none are running/paused and one is stopped.
422a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     *         HAS_DESTROYED_ACTIVITIES if none are running/paused/stopped.
423a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
424a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static int determineApplicationState() {
425a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        boolean hasPausedActivity = false;
426a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        boolean hasStoppedActivity = false;
427a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
428a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        for (ActivityInfo info : sActivityInfo.values()) {
429a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            int state = info.getStatus();
430a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            if (state != ActivityState.PAUSED
431a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    && state != ActivityState.STOPPED
432a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    && state != ActivityState.DESTROYED) {
433a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                return ApplicationState.HAS_RUNNING_ACTIVITIES;
434a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            } else if (state == ActivityState.PAUSED) {
435a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                hasPausedActivity = true;
436a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            } else if (state == ActivityState.STOPPED) {
437a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                hasStoppedActivity = true;
438a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            }
439a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
440a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
441a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (hasPausedActivity) return ApplicationState.HAS_PAUSED_ACTIVITIES;
442a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (hasStoppedActivity) return ApplicationState.HAS_STOPPED_ACTIVITIES;
443a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return ApplicationState.HAS_DESTROYED_ACTIVITIES;
444a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
445a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
446a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Called to notify the native side of state changes.
447a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // IMPORTANT: This is always called on the main thread!
448a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static native void nativeOnApplicationStateChange(int newState);
449a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
450