Feature to listen on window focus events (#25039)

Summary:
Addressed issue: https://github.com/facebook/react-native/issues/24149

On Android, activity's lifecycle events are not triggered when the user pulls down the Status Bar (opening Notification Drawer). In order to know that, you need to override [onWindowFocusChanged method](https://developer.android.com/reference/android/app/Activity.html#onWindowFocusChanged(boolean)).

## Changelog

[Android] [Added] - Adds a new listener for `onWindowFocusChanged`
[JavaScript] [Added] - New event, `focusChanged`, to listen on focus gain/loss
Pull Request resolved: https://github.com/facebook/react-native/pull/25039

Differential Revision: D15644954

Pulled By: cpojer

fbshipit-source-id: 823acffc4287bec4bf56e9f5ffcac65c01cf13d3
This commit is contained in:
Krzysztof Borowy
2019-06-05 16:05:34 -07:00
committed by Facebook Github Bot
parent bf8d918681
commit d45818fe47
7 changed files with 106 additions and 19 deletions
@@ -125,6 +125,12 @@ public abstract class ReactActivity extends AppCompatActivity
mDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
mDelegate.onWindowFocusChanged(hasFocus);
}
protected final ReactNativeHost getReactNativeHost() {
return mDelegate.getReactNativeHost();
}
@@ -182,6 +182,12 @@ public class ReactActivityDelegate {
return false;
}
public void onWindowFocusChanged(boolean hasFocus) {
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onWindowFocusChange(hasFocus);
}
}
@TargetApi(Build.VERSION_CODES.M)
public void requestPermissions(
String[] permissions,
@@ -703,6 +703,15 @@ public class ReactInstanceManager {
}
}
@ThreadConfined(UI)
public void onWindowFocusChange(boolean hasFocus) {
UiThreadUtil.assertOnUiThread();
ReactContext currentContext = getCurrentReactContext();
if (currentContext != null) {
currentContext.onWindowFocusChange(hasFocus);
}
}
@ThreadConfined(UI)
public void showDevOptionsDialog() {
UiThreadUtil.assertOnUiThread();
@@ -36,6 +36,8 @@ public class ReactContext extends ContextWrapper {
new CopyOnWriteArraySet<>();
private final CopyOnWriteArraySet<ActivityEventListener> mActivityEventListeners =
new CopyOnWriteArraySet<>();
private final CopyOnWriteArraySet<WindowFocusChangeListener> mWindowFocusEventListeners =
new CopyOnWriteArraySet<>();
private LifecycleState mLifecycleState = LifecycleState.BEFORE_CREATE;
@@ -196,6 +198,14 @@ public class ReactContext extends ContextWrapper {
mActivityEventListeners.remove(listener);
}
public void addWindowFocusChangeListener(WindowFocusChangeListener listener) {
mWindowFocusEventListeners.add(listener);
}
public void removeWindowFocusChangeListener(WindowFocusChangeListener listener) {
mWindowFocusEventListeners.remove(listener);
}
/**
* Should be called by the hosting Fragment in {@link Fragment#onResume}
*/
@@ -281,6 +291,17 @@ public class ReactContext extends ContextWrapper {
}
}
public void onWindowFocusChange(boolean hasFocus) {
UiThreadUtil.assertOnUiThread();
for (WindowFocusChangeListener listener : mWindowFocusEventListeners) {
try {
listener.onWindowFocusChange(hasFocus);
} catch (RuntimeException e) {
handleException(e);
}
}
}
public void assertOnUiQueueThread() {
Assertions.assertNotNull(mUiMessageQueueThread).assertIsOnThread();
}
@@ -0,0 +1,15 @@
// Copyright (c) Facebook, Inc. and its affiliates.
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
package com.facebook.react.bridge;
/*
* Listener for receiving window focus events.
*/
public interface WindowFocusChangeListener {
void onWindowFocusChange(boolean hasFocus);
}
@@ -13,6 +13,7 @@ import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WindowFocusChangeListener;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.module.annotations.ReactModule;
@@ -23,7 +24,7 @@ import java.util.Map;
@ReactModule(name = AppStateModule.NAME)
public class AppStateModule extends ReactContextBaseJavaModule
implements LifecycleEventListener {
implements LifecycleEventListener, WindowFocusChangeListener {
protected static final String NAME = "AppState";
@@ -37,6 +38,7 @@ public class AppStateModule extends ReactContextBaseJavaModule
public AppStateModule(ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addLifecycleEventListener(this);
reactContext.addWindowFocusChangeListener(this);
mAppState = (reactContext.getLifecycleState() == LifecycleState.RESUMED ?
APP_STATE_ACTIVE : APP_STATE_BACKGROUND);
}
@@ -76,6 +78,12 @@ public class AppStateModule extends ReactContextBaseJavaModule
// catalyst instance is going to be immediately dropped, and all JS calls with it.
}
@Override
public void onWindowFocusChange(boolean hasFocus) {
getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class)
.emit("appStateFocusChange", hasFocus);
}
private WritableMap createAppStateEventMap() {
WritableMap appState = Arguments.createMap();
appState.putString("app_state", mAppState);