RN refactor immersive mode (#13583)

* fix(android): refactor immersive mode
This commit is contained in:
Saúl Ibarra Corretgé
2023-07-19 11:00:47 +02:00
committed by GitHub
parent d335438f12
commit ec9fcdf1cb
14 changed files with 63 additions and 120 deletions

View File

@@ -82,7 +82,7 @@ dependencies {
if (!rootProject.ext.libreBuild) {
// Sync with react-native-google-signin
implementation 'com.google.android.gms:play-services-auth:19.0.2'
implementation 'com.google.android.gms:play-services-auth:19.2.0'
// Firebase
// - Crashlytics

View File

@@ -74,7 +74,7 @@ dependencies {
}
implementation project(':react-native-gesture-handler')
implementation project(':react-native-get-random-values')
implementation project(':react-native-immersive')
implementation project(':react-native-immersive-mode')
implementation project(':react-native-keep-awake')
implementation project(':react-native-orientation-locker')
implementation project(':react-native-pager-view')

View File

@@ -26,7 +26,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.ReactRootView;
import com.rnimmersive.RNImmersiveModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
@@ -229,22 +228,4 @@ public class JitsiMeetView extends FrameLayout {
dispose();
super.onDetachedFromWindow();
}
/**
* Called when the window containing this view gains or loses focus.
*
* @param hasFocus If the window of this view now has focus, {@code true};
* otherwise, {@code false}.
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
// https://github.com/mockingbot/react-native-immersive#restore-immersive-state
RNImmersiveModule immersive = RNImmersiveModule.getInstance();
if (hasFocus && immersive != null) {
immersive.emitImmersiveStateChangeEvent();
}
}
}

View File

@@ -105,7 +105,7 @@ class ReactInstanceManagerHolder {
new com.oney.WebRTCModule.WebRTCModulePackage(),
new com.swmansion.gesturehandler.RNGestureHandlerPackage(),
new org.linusu.RNGetRandomValuesPackage(),
new com.rnimmersive.RNImmersivePackage(),
new com.rnimmersivemode.RNImmersiveModePackage(),
new com.swmansion.rnscreens.RNScreensPackage(),
new com.zmxv.RNSound.RNSoundPackage(),
new com.th3rdwave.safeareacontext.SafeAreaContextPackage(),

View File

@@ -27,8 +27,8 @@ include ':react-native-giphy'
project(':react-native-giphy').projectDir = new File(rootProject.projectDir, '../node_modules/@giphy/react-native-sdk/android')
include ':react-native-google-signin'
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-google-signin/google-signin/android')
include ':react-native-immersive'
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
include ':react-native-immersive-mode'
project(':react-native-immersive-mode').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive-mode/android')
include ':react-native-keep-awake'
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
include ':react-native-orientation-locker'

20
package-lock.json generated
View File

@@ -83,7 +83,7 @@
"react-native-dialog": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
"react-native-gesture-handler": "2.9.0",
"react-native-get-random-values": "1.7.2",
"react-native-immersive": "2.0.0",
"react-native-immersive-mode": "2.0.1",
"react-native-keep-awake": "4.0.0",
"react-native-orientation-locker": "1.5.0",
"react-native-pager-view": "5.4.9",
@@ -15812,12 +15812,12 @@
"resolved": "https://registry.npmjs.org/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.7.tgz",
"integrity": "sha512-+4JpbIx42zGTONhBTIXSyfyHICHC29VTvhkkoUOJAh/XHPEixpuBduYgf6Y4y9wsN1ARlQhBBoptTvXvAFQf5g=="
},
"node_modules/react-native-immersive": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-native-immersive/-/react-native-immersive-2.0.0.tgz",
"integrity": "sha512-9TL05nTHN/x9sN1wbUlBoGyzH4NCuZ/7WEEUp5CvOoKuUABvdYosov0O0SAMbm/5J913RRoy98VB6tGNi9lRSw==",
"node_modules/react-native-immersive-mode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/react-native-immersive-mode/-/react-native-immersive-mode-2.0.1.tgz",
"integrity": "sha512-2wlL7VIHl4rr4gwgnUp9K1UvsN7J5VCGqoAvBWQXvB4xn7XaoDEl6z9vqaqOiEdC6aAh2d/7zqcJz+dfcR2ELw==",
"peerDependencies": {
"react-native": ">=0.47.0"
"react-native": ">=0.60.5"
}
},
"node_modules/react-native-keep-awake": {
@@ -31587,10 +31587,10 @@
"resolved": "https://registry.npmjs.org/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.7.tgz",
"integrity": "sha512-+4JpbIx42zGTONhBTIXSyfyHICHC29VTvhkkoUOJAh/XHPEixpuBduYgf6Y4y9wsN1ARlQhBBoptTvXvAFQf5g=="
},
"react-native-immersive": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-native-immersive/-/react-native-immersive-2.0.0.tgz",
"integrity": "sha512-9TL05nTHN/x9sN1wbUlBoGyzH4NCuZ/7WEEUp5CvOoKuUABvdYosov0O0SAMbm/5J913RRoy98VB6tGNi9lRSw=="
"react-native-immersive-mode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/react-native-immersive-mode/-/react-native-immersive-mode-2.0.1.tgz",
"integrity": "sha512-2wlL7VIHl4rr4gwgnUp9K1UvsN7J5VCGqoAvBWQXvB4xn7XaoDEl6z9vqaqOiEdC6aAh2d/7zqcJz+dfcR2ELw=="
},
"react-native-keep-awake": {
"version": "4.0.0",

View File

@@ -89,7 +89,7 @@
"react-native-dialog": "https://github.com/jitsi/react-native-dialog/releases/download/v9.2.2-jitsi.1/react-native-dialog-9.2.2.tgz",
"react-native-gesture-handler": "2.9.0",
"react-native-get-random-values": "1.7.2",
"react-native-immersive": "2.0.0",
"react-native-immersive-mode": "2.0.1",
"react-native-keep-awake": "4.0.0",
"react-native-orientation-locker": "1.5.0",
"react-native-pager-view": "5.4.9",

View File

@@ -1,19 +0,0 @@
diff --git a/node_modules/react-native-immersive/index.js b/node_modules/react-native-immersive/index.js
index 55dab57..110260b 100644
--- a/node_modules/react-native-immersive/index.js
+++ b/node_modules/react-native-immersive/index.js
@@ -18,7 +18,13 @@ const Immersive = Platform.OS === 'android' ? {
isListenerEnabled = true
RNImmersive.addImmersiveListener()
},
- removeImmersiveListener: (listener) => DeviceEventEmitter.removeListener('@@IMMERSIVE_STATE_CHANGED', listener)
+ removeImmersiveListener: (listener) => {
+ const immersiveListener = DeviceEventEmitter.addListener('@@IMMERSIVE_STATE_CHANGED', listener);
+
+ return () => {
+ immersiveListener.remove();
+ }
+ }
} : {
on: unSupportedError,
off: unSupportedError,

View File

@@ -1,5 +1,4 @@
import { AppState } from 'react-native';
import { AnyAction } from 'redux';
import { IStore } from '../../app/types';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app/actionTypes';
@@ -23,12 +22,12 @@ MiddlewareRegistry.register(store => next => action => {
case APP_WILL_MOUNT: {
const { dispatch } = store;
_setAppStateListener(store, next, action, _onAppStateChange.bind(undefined, dispatch));
_setAppStateListener(store, _onAppStateChange.bind(undefined, dispatch));
break;
}
case APP_WILL_UNMOUNT:
_setAppStateListener(store, next, action, undefined);
_setAppStateListener(store, undefined);
break;
}
@@ -55,20 +54,14 @@ function _onAppStateChange(dispatch: IStore['dispatch'], appState: string) {
*
* @param {Store} store - The redux store in which the specified action is being
* dispatched.
* @param {Dispatch} next - The redux dispatch function to dispatch the
* specified action to the specified store.
* @param {Action} action - The redux action {@code _SET_IMMERSIVE_LISTENER}
* which is being dispatched in the specified store.
* @param {any} listener - Listener for app state status.
* @private
* @returns {Object} The value returned by {@code next(action)}.
*/
function _setAppStateListener({ dispatch, getState }: IStore, next: Function, action: AnyAction, listener: any) {
function _setAppStateListener({ dispatch, getState }: IStore, listener: any) {
const { subscription } = getState()['features/background'];
const result = next(action);
subscription?.remove();
listener && dispatch(_setAppStateSubscription(AppState.addEventListener('change', listener)));
return result;
dispatch(_setAppStateSubscription(listener ? AppState.addEventListener('change', listener) : undefined));
}

View File

@@ -1,12 +1,12 @@
/**
* The type of (redux) action to set the react-native-immersive's change event
* listener.
* subscription.
*
* {
* type: _SET_IMMERSIVE_LISTENER,
* listener: Function
* type: _SET_IMMERSIVE_SUBSCRIPTION,
* subscription: Function
* }
*
* @protected
*/
export const _SET_IMMERSIVE_LISTENER = '_SET_IMMERSIVE_LISTENER';
export const _SET_IMMERSIVE_SUBSCRIPTION = '_SET_IMMERSIVE_SUBSCRIPTION';

View File

@@ -1,19 +1,21 @@
import { _SET_IMMERSIVE_LISTENER } from './actionTypes';
import { NativeEventSubscription } from 'react-native';
import { _SET_IMMERSIVE_SUBSCRIPTION } from './actionTypes';
/**
* Sets the change event listener to be used with react-native-immersive's API.
*
* @param {Function} [listener] - The function to be used with
* @param {Function} subscription - The function to be used with
* react-native-immersive's API as the change event listener.
* @protected
* @returns {{
* type: _SET_IMMERSIVE_LISTENER,
* listener: ?Function
* type: _SET_IMMERSIVE_SUBSCRIPTION,
* subscription: ?NativeEventSubscription
* }}
*/
export function _setImmersiveListener(listener?: Function) {
export function _setImmersiveSubscription(subscription?: NativeEventSubscription) {
return {
type: _SET_IMMERSIVE_LISTENER,
listener
type: _SET_IMMERSIVE_SUBSCRIPTION,
subscription
};
}

View File

@@ -0,0 +1,3 @@
import { getLogger } from '../../base/logging/functions';
export default getLogger('features/full-screen');

View File

@@ -1,6 +1,4 @@
// @ts-expect-error
import { Immersive } from 'react-native-immersive';
import { AnyAction } from 'redux';
import ImmersiveMode from 'react-native-immersive-mode';
import { IStore } from '../../app/types';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app/actionTypes';
@@ -8,12 +6,18 @@ import { getCurrentConference } from '../../base/conference/functions';
import { isAnyDialogOpen } from '../../base/dialog/functions';
import { FULLSCREEN_ENABLED } from '../../base/flags/constants';
import { getFeatureFlag } from '../../base/flags/functions';
import Platform from '../../base/react/Platform.native';
import MiddlewareRegistry from '../../base/redux/MiddlewareRegistry';
import StateListenerRegistry from '../../base/redux/StateListenerRegistry';
import { _SET_IMMERSIVE_LISTENER } from './actionTypes';
import { _setImmersiveListener as _setImmersiveListenerA } from './actions';
import { _setImmersiveSubscription } from './actions';
import logger from './logger';
type BarVisibilityType = {
navigationBottomBar: boolean;
statusBar: boolean;
};
type ImmersiveListener = (visibility: BarVisibilityType) => void;
/**
* Middleware that captures conference actions and activates or deactivates the
@@ -28,20 +32,14 @@ import { _setImmersiveListener as _setImmersiveListenerA } from './actions';
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case _SET_IMMERSIVE_LISTENER:
return _setImmersiveListenerF(store, next, action);
case APP_WILL_MOUNT: {
const result = next(action);
_setImmersiveListener(store, _onImmersiveChange.bind(undefined, store));
store.dispatch(
_setImmersiveListenerA(_onImmersiveChange.bind(undefined, store)));
return result;
break;
}
case APP_WILL_UNMOUNT:
store.dispatch(_setImmersiveListenerA(undefined));
_setImmersiveListener(store, undefined);
break;
}
@@ -95,11 +93,9 @@ function _onImmersiveChange({ getState }: IStore) {
* @returns {void}
*/
function _setFullScreen(fullScreen: boolean) {
// XXX The React Native module Immersive is only implemented on Android and
// throws on other platforms.
if (Platform.OS === 'android') {
fullScreen ? Immersive.on() : Immersive.off();
}
logger.info(`Setting full-screen mode: ${fullScreen}`);
ImmersiveMode.fullLayout(fullScreen);
ImmersiveMode.setBarMode(fullScreen ? 'Full' : 'Normal');
}
/**
@@ -109,29 +105,14 @@ function _setFullScreen(fullScreen: boolean) {
*
* @param {Store} store - The redux store in which the specified action is being
* dispatched.
* @param {Dispatch} next - The redux dispatch function to dispatch the
* specified action to the specified store.
* @param {Action} action - The redux action {@code _SET_IMMERSIVE_LISTENER}
* which is being dispatched in the specified store.
* @param {Function} listener - Listener for immersive state.
* @private
* @returns {Object} The value returned by {@code next(action)}.
*/
function _setImmersiveListenerF({ getState }: IStore, next: Function, action: AnyAction) {
// XXX The React Native module Immersive is only implemented on Android and
// throws on other platforms.
if (Platform.OS === 'android') {
// Remove the old Immersive listener and add the new one.
const { listener: oldListener } = getState()['features/full-screen'];
const result = next(action);
const { listener: newListener } = getState()['features/full-screen'];
function _setImmersiveListener({ dispatch, getState }: IStore, listener?: ImmersiveListener) {
const { subscription } = getState()['features/full-screen'];
if (oldListener !== newListener) {
oldListener && Immersive.removeImmersiveListener(oldListener);
newListener && Immersive.addImmersiveListener(newListener);
}
subscription?.remove();
return result;
}
return next(action);
dispatch(_setImmersiveSubscription(listener ? ImmersiveMode.addEventListener(listener) : undefined));
}

View File

@@ -1,17 +1,19 @@
import { NativeEventSubscription } from 'react-native';
import ReducerRegistry from '../../base/redux/ReducerRegistry';
import { _SET_IMMERSIVE_LISTENER } from './actionTypes';
import { _SET_IMMERSIVE_SUBSCRIPTION } from './actionTypes';
export interface IFullScreenState {
listener?: Function;
subscription?: NativeEventSubscription;
}
ReducerRegistry.register<IFullScreenState>('features/full-screen', (state = {}, action): IFullScreenState => {
switch (action.type) {
case _SET_IMMERSIVE_LISTENER:
case _SET_IMMERSIVE_SUBSCRIPTION:
return {
...state,
listener: action.listener
subscription: action.subscription
};
}