ApplicationStatus.java revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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