From ec9fcdf1cb3b50ef8fe267179a42444727353fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Wed, 19 Jul 2023 11:00:47 +0200 Subject: [PATCH] RN refactor immersive mode (#13583) * fix(android): refactor immersive mode --- android/app/build.gradle | 2 +- android/sdk/build.gradle | 2 +- .../org/jitsi/meet/sdk/JitsiMeetView.java | 19 ------ .../meet/sdk/ReactInstanceManagerHolder.java | 2 +- android/settings.gradle | 4 +- package-lock.json | 20 +++--- package.json | 2 +- patches/react-native-immersive+2.0.0.patch | 19 ------ .../mobile/background/middleware.native.ts | 15 ++--- .../mobile/full-screen/actionTypes.ts | 8 +-- react/features/mobile/full-screen/actions.ts | 16 ++--- react/features/mobile/full-screen/logger.ts | 3 + .../features/mobile/full-screen/middleware.ts | 61 +++++++------------ react/features/mobile/full-screen/reducer.ts | 10 +-- 14 files changed, 63 insertions(+), 120 deletions(-) delete mode 100644 patches/react-native-immersive+2.0.0.patch create mode 100644 react/features/mobile/full-screen/logger.ts diff --git a/android/app/build.gradle b/android/app/build.gradle index 3c1779da86..8dbec279a8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -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 diff --git a/android/sdk/build.gradle b/android/sdk/build.gradle index a29acdbbad..a01af897fc 100644 --- a/android/sdk/build.gradle +++ b/android/sdk/build.gradle @@ -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') diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java index 59d6e6b4f3..3c523e4403 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java @@ -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(); - } - } } diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java index 900c470a8b..b7df98735c 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java @@ -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(), diff --git a/android/settings.gradle b/android/settings.gradle index d033c072f6..a8b4880815 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -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' diff --git a/package-lock.json b/package-lock.json index 662552e456..ce7c52c02f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index cb8e442c46..fd7da18ef1 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/patches/react-native-immersive+2.0.0.patch b/patches/react-native-immersive+2.0.0.patch deleted file mode 100644 index 206183ea56..0000000000 --- a/patches/react-native-immersive+2.0.0.patch +++ /dev/null @@ -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, diff --git a/react/features/mobile/background/middleware.native.ts b/react/features/mobile/background/middleware.native.ts index b001013214..21f7eb6e7b 100644 --- a/react/features/mobile/background/middleware.native.ts +++ b/react/features/mobile/background/middleware.native.ts @@ -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)); } diff --git a/react/features/mobile/full-screen/actionTypes.ts b/react/features/mobile/full-screen/actionTypes.ts index 6e9f3ac9c5..682d77df6e 100644 --- a/react/features/mobile/full-screen/actionTypes.ts +++ b/react/features/mobile/full-screen/actionTypes.ts @@ -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'; diff --git a/react/features/mobile/full-screen/actions.ts b/react/features/mobile/full-screen/actions.ts index e09209d9f0..12950bfca8 100644 --- a/react/features/mobile/full-screen/actions.ts +++ b/react/features/mobile/full-screen/actions.ts @@ -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 }; } diff --git a/react/features/mobile/full-screen/logger.ts b/react/features/mobile/full-screen/logger.ts new file mode 100644 index 0000000000..fba4eb8b7e --- /dev/null +++ b/react/features/mobile/full-screen/logger.ts @@ -0,0 +1,3 @@ +import { getLogger } from '../../base/logging/functions'; + +export default getLogger('features/full-screen'); diff --git a/react/features/mobile/full-screen/middleware.ts b/react/features/mobile/full-screen/middleware.ts index 72f72bf7d3..65de61861f 100644 --- a/react/features/mobile/full-screen/middleware.ts +++ b/react/features/mobile/full-screen/middleware.ts @@ -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)); } diff --git a/react/features/mobile/full-screen/reducer.ts b/react/features/mobile/full-screen/reducer.ts index d86eb0ad8b..4cee7c8a15 100644 --- a/react/features/mobile/full-screen/reducer.ts +++ b/react/features/mobile/full-screen/reducer.ts @@ -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('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 }; }