|
17 | 17 | import static com.google.firebase.firestore.util.Assert.hardAssert;
|
18 | 18 |
|
19 | 19 | import android.annotation.TargetApi;
|
| 20 | +import android.app.Activity; |
| 21 | +import android.app.Application; |
20 | 22 | import android.content.BroadcastReceiver;
|
| 23 | +import android.content.ComponentCallbacks2; |
21 | 24 | import android.content.Context;
|
22 | 25 | import android.content.Intent;
|
23 | 26 | import android.content.IntentFilter;
|
| 27 | +import android.content.res.Configuration; |
24 | 28 | import android.net.ConnectivityManager;
|
25 | 29 | import android.net.Network;
|
26 | 30 | import android.os.Build;
|
| 31 | +import android.os.Bundle; |
| 32 | +import androidx.annotation.NonNull; |
27 | 33 | import androidx.annotation.Nullable;
|
28 |
| -import com.google.android.gms.common.api.internal.BackgroundDetector; |
29 | 34 | import com.google.firebase.firestore.util.Consumer;
|
| 35 | +import com.google.firebase.firestore.util.Logger; |
30 | 36 | import java.util.ArrayList;
|
31 | 37 | import java.util.List;
|
| 38 | +import java.util.concurrent.atomic.AtomicBoolean; |
32 | 39 |
|
33 | 40 | /**
|
34 | 41 | * Android implementation of ConnectivityMonitor. Parallel implementations exist for N+ and pre-N.
|
35 | 42 | *
|
36 | 43 | * <p>Implementation note: Most of the code here was shamelessly stolen from
|
37 | 44 | * https://github.com/grpc/grpc-java/blob/master/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java
|
38 | 45 | */
|
39 |
| -public final class AndroidConnectivityMonitor |
40 |
| - implements ConnectivityMonitor, BackgroundDetector.BackgroundStateChangeListener { |
| 46 | +public final class AndroidConnectivityMonitor implements ConnectivityMonitor { |
| 47 | + |
| 48 | + private static final String LOG_TAG = "AndroidConnectivityMonitor"; |
41 | 49 |
|
42 | 50 | private final Context context;
|
43 | 51 | @Nullable private final ConnectivityManager connectivityManager;
|
@@ -78,34 +86,80 @@ private void configureNetworkMonitoring() {
|
78 | 86 | final DefaultNetworkCallback defaultNetworkCallback = new DefaultNetworkCallback();
|
79 | 87 | connectivityManager.registerDefaultNetworkCallback(defaultNetworkCallback);
|
80 | 88 | unregisterRunnable =
|
81 |
| - new Runnable() { |
82 |
| - @Override |
83 |
| - public void run() { |
84 |
| - connectivityManager.unregisterNetworkCallback(defaultNetworkCallback); |
85 |
| - } |
86 |
| - }; |
| 89 | + () -> connectivityManager.unregisterNetworkCallback(defaultNetworkCallback); |
87 | 90 | } else {
|
88 | 91 | NetworkReceiver networkReceiver = new NetworkReceiver();
|
89 | 92 | @SuppressWarnings("deprecation")
|
90 | 93 | IntentFilter networkIntentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
|
91 | 94 | context.registerReceiver(networkReceiver, networkIntentFilter);
|
92 |
| - unregisterRunnable = |
93 |
| - new Runnable() { |
94 |
| - @Override |
95 |
| - public void run() { |
96 |
| - context.unregisterReceiver(networkReceiver); |
97 |
| - } |
98 |
| - }; |
| 95 | + unregisterRunnable = () -> context.unregisterReceiver(networkReceiver); |
99 | 96 | }
|
100 | 97 | }
|
101 | 98 |
|
102 | 99 | private void configureBackgroundStateListener() {
|
103 |
| - BackgroundDetector.getInstance().addListener(this); |
| 100 | + Application application = (Application) context.getApplicationContext(); |
| 101 | + final AtomicBoolean inBackground = new AtomicBoolean(); |
| 102 | + |
| 103 | + // Manually register an ActivityLifecycleCallback. Android's BackgroundDetector only notifies |
| 104 | + // when it is certain that the app transitioned from background to foreground. Instead, we |
| 105 | + // want to be notified whenever there is a slight chance that this transition happened. |
| 106 | + application.registerActivityLifecycleCallbacks( |
| 107 | + new Application.ActivityLifecycleCallbacks() { |
| 108 | + @Override |
| 109 | + public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) { |
| 110 | + if (inBackground.compareAndSet(true, false)) { |
| 111 | + raiseForegroundNotification(); |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + @Override |
| 116 | + public void onActivityStarted(@NonNull Activity activity) { |
| 117 | + if (inBackground.compareAndSet(true, false)) { |
| 118 | + raiseForegroundNotification(); |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + @Override |
| 123 | + public void onActivityResumed(@NonNull Activity activity) { |
| 124 | + if (inBackground.compareAndSet(true, false)) { |
| 125 | + raiseForegroundNotification(); |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + @Override |
| 130 | + public void onActivityPaused(@NonNull Activity activity) {} |
| 131 | + |
| 132 | + @Override |
| 133 | + public void onActivityStopped(@NonNull Activity activity) {} |
| 134 | + |
| 135 | + @Override |
| 136 | + public void onActivitySaveInstanceState( |
| 137 | + @NonNull Activity activity, @NonNull Bundle outState) {} |
| 138 | + |
| 139 | + @Override |
| 140 | + public void onActivityDestroyed(@NonNull Activity activity) {} |
| 141 | + }); |
| 142 | + |
| 143 | + application.registerComponentCallbacks( |
| 144 | + new ComponentCallbacks2() { |
| 145 | + @Override |
| 146 | + public void onTrimMemory(int level) { |
| 147 | + if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { |
| 148 | + inBackground.set(true); |
| 149 | + } |
| 150 | + } |
| 151 | + |
| 152 | + @Override |
| 153 | + public void onConfigurationChanged(@NonNull Configuration newConfig) {} |
| 154 | + |
| 155 | + @Override |
| 156 | + public void onLowMemory() {} |
| 157 | + }); |
104 | 158 | }
|
105 | 159 |
|
106 |
| - @Override |
107 |
| - public void onBackgroundStateChanged(boolean background) { |
108 |
| - if (!background && isConnected()) { |
| 160 | + public void raiseForegroundNotification() { |
| 161 | + Logger.debug(LOG_TAG, "App has entered the foreground."); |
| 162 | + if (isConnected()) { |
109 | 163 | raiseCallbacks(/* connected= */ true);
|
110 | 164 | }
|
111 | 165 | }
|
|
0 commit comments