[RN] Make full-screen more resilient on Android (Coding style: consistency)

This commit is contained in:
Lyubo Marinov 2018-02-14 12:28:22 -06:00
parent 4757c1ebca
commit e2cf7a788d
12 changed files with 164 additions and 120 deletions

View File

@ -416,20 +416,20 @@ public class JitsiMeetView extends FrameLayout {
}
/**
* Handler for focus changes which the window where this view is attached to
* is experiencing. Here we call into the Immersive mode plugin, so it
* triggers an event.
* Called when the window containing this view gains or loses focus.
*
* @param hasFocus - Whether the window / view has focus or not.
* @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);
RNImmersiveModule module = RNImmersiveModule.getInstance();
// https://github.com/mockingbot/react-native-immersive#restore-immersive-state
RNImmersiveModule immersive = RNImmersiveModule.getInstance();
if (hasFocus && module != null) {
module.emitImmersiveStateChangeEvent();
if (hasFocus && immersive != null) {
immersive.emitImmersiveStateChangeEvent();
}
}

View File

@ -5,7 +5,7 @@ import { equals, set, ReducerRegistry } from '../redux';
import { SET_CALLEE_INFO_VISIBLE, SET_JWT } from './actionTypes';
/**
* The initial redux state of the feature jwt.
* The default/initial redux state of the feature jwt.
*
* @private
* @type {{
@ -13,7 +13,7 @@ import { SET_CALLEE_INFO_VISIBLE, SET_JWT } from './actionTypes';
* isGuest: boolean
* }}
*/
const _INITIAL_STATE = {
const DEFAULT_STATE = {
/**
* The indicator which determines whether (the) {@code CalleeInfo} is
* visible.
@ -42,7 +42,7 @@ const _INITIAL_STATE = {
*/
ReducerRegistry.register(
'features/base/jwt',
(state = _INITIAL_STATE, action) => {
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case SET_CALLEE_INFO_VISIBLE:
return set(state, 'calleeInfoVisible', action.calleeInfoVisible);
@ -51,7 +51,7 @@ ReducerRegistry.register(
// eslint-disable-next-line no-unused-vars
const { type, ...payload } = action;
const nextState = {
..._INITIAL_STATE,
...DEFAULT_STATE,
...payload
};

View File

@ -1,3 +1,5 @@
// @flow
import { ReducerRegistry } from '../redux';
import {
@ -8,18 +10,18 @@ import {
} from './actionTypes';
/**
* The initial state of the feature base/lib-jitsi-meet.
* The default/initial redux state of the feature base/lib-jitsi-meet.
*
* @type {Object}
*/
const INITIAL_STATE = {};
const DEFAULT_STATE = {};
ReducerRegistry.register(
'features/base/lib-jitsi-meet',
(state = INITIAL_STATE, action) => {
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case LIB_DID_DISPOSE:
return INITIAL_STATE;
return DEFAULT_STATE;
case LIB_DID_INIT:
return {

View File

@ -1,18 +1,20 @@
// @flow
import { equals, ReducerRegistry } from '../redux';
import { SET_LOGGING_CONFIG } from './actionTypes';
/**
* The initial state of the feature base/logging.
* The default/initial redux state of the feature base/logging.
*
* XXX When making any changes to the INITIAL_STATE make sure to also update
* XXX When making any changes to the DEFAULT_STATE make sure to also update
* logging_config.js file located in the root directory of this project !!!
*
* @type {{
* config: Object
* }}
*/
const INITIAL_STATE = {
const DEFAULT_STATE = {
config: {
defaultLogLevel: 'trace',
@ -26,7 +28,7 @@ const INITIAL_STATE = {
ReducerRegistry.register(
'features/base/logging',
(state = INITIAL_STATE, action) => {
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case SET_LOGGING_CONFIG:
return _setLoggingConfig(state, action);
@ -48,9 +50,9 @@ ReducerRegistry.register(
*/
function _setLoggingConfig(state, action) {
const config = {
// The config of INITIAL_STATE is the default configuration of the
// The config of DEFAULT_STATE is the default configuration of the
// feature base/logging.
...INITIAL_STATE.config,
...DEFAULT_STATE.config,
...action.config
};

View File

@ -1,19 +1,21 @@
// @flow
import { ReducerRegistry, set } from '../redux';
import { SET_ASPECT_RATIO, SET_REDUCED_UI } from './actionTypes';
import { ASPECT_RATIO_NARROW } from './constants';
/**
* The initial redux state of the feature base/responsive-ui.
* The default/initial redux state of the feature base/responsive-ui.
*/
const _INITIAL_STATE = {
const DEFAULT_STATE = {
aspectRatio: ASPECT_RATIO_NARROW,
reducedUI: false
};
ReducerRegistry.register(
'features/base/responsive-ui',
(state = _INITIAL_STATE, action) => {
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case SET_ASPECT_RATIO:
return set(state, 'aspectRatio', action.aspectRatio);

View File

@ -1,16 +1,13 @@
/* @flow */
// @flow
import { AppState } from 'react-native';
import type { Dispatch } from 'redux';
import {
APP_WILL_MOUNT,
APP_WILL_UNMOUNT
} from '../../app';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../app';
import { MiddlewareRegistry } from '../../base/redux';
import {
_setAppStateListener,
_setAppStateListener as _setAppStateListenerA,
_setBackgroundVideoMuted,
appStateChanged
} from './actions';
@ -25,39 +22,29 @@ import {
* required to mute or unmute the local video in case the application goes to
* the background or comes back from it.
*
* @param {Store} store - Redux store.
* @param {Store} store - The redux store.
* @returns {Function}
* @see {@link https://facebook.github.io/react-native/docs/appstate.html}
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case _SET_APP_STATE_LISTENER: {
// Remove the current/old AppState listener.
const { appStateListener } = store.getState()['features/background'];
if (appStateListener) {
AppState.removeEventListener('change', appStateListener);
}
// Add the new AppState listener.
if (action.listener) {
AppState.addEventListener('change', action.listener);
}
break;
}
case _SET_APP_STATE_LISTENER:
return _setAppStateListenerF(store, next, action);
case APP_STATE_CHANGED:
_appStateChanged(store.dispatch, action.appState);
break;
case APP_WILL_MOUNT:
store.dispatch(
_setAppStateListener(
_onAppStateChange.bind(undefined, store.dispatch)));
case APP_WILL_MOUNT: {
const { dispatch } = store;
dispatch(
_setAppStateListenerA(_onAppStateChange.bind(undefined, dispatch)));
break;
}
case APP_WILL_UNMOUNT:
store.dispatch(_setAppStateListener(null));
store.dispatch(_setAppStateListenerA(undefined));
break;
}
@ -65,11 +52,11 @@ MiddlewareRegistry.register(store => next => action => {
});
/**
* Handles app state changes. Dispatches the necessary Redux actions for the
* Handles app state changes. Dispatches the necessary redux actions for the
* local video to be muted when the app goes to the background, and to be
* unmuted when the app comes back.
*
* @param {Dispatch} dispatch - Redux dispatch function.
* @param {Dispatch} dispatch - The redux {@code dispatch} function.
* @param {string} appState - The current app state.
* @private
* @returns {void}
@ -97,9 +84,9 @@ function _appStateChanged(dispatch: Function, appState: string) {
/**
* Called by React Native's AppState API to notify that the application state
* has changed. Dispatches the change within the (associated) Redux store.
* has changed. Dispatches the change within the (associated) redux store.
*
* @param {Dispatch} dispatch - Redux dispatch function.
* @param {Dispatch} dispatch - The redux {@code dispatch} function.
* @param {string} appState - The current application execution state.
* @private
* @returns {void}
@ -107,3 +94,31 @@ function _appStateChanged(dispatch: Function, appState: string) {
function _onAppStateChange(dispatch: Dispatch<*>, appState: string) {
dispatch(appStateChanged(appState));
}
/**
* Notifies the feature filmstrip that the action
* {@link _SET_IMMERSIVE_LISTENER} is being dispatched within a specific redux
* store.
*
* @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.
* @private
* @returns {Object} The value returned by {@code next(action)}.
*/
function _setAppStateListenerF({ getState }, next, action) {
// Remove the old AppState listener and add the new one.
const { appStateListener: oldListener } = getState()['features/background'];
const result = next(action);
const { appStateListener: newListener } = getState()['features/background'];
if (oldListener !== newListener) {
oldListener && AppState.removeEventListener('change', oldListener);
newListener && AppState.addEventListener('change', newListener);
}
return result;
}

View File

@ -1,3 +1,5 @@
// @flow
import { ReducerRegistry } from '../../base/redux';
import {
@ -5,13 +7,16 @@ import {
APP_STATE_CHANGED
} from './actionTypes';
const INITIAL_STATE = {
/**
* The default/initial redux state of the feature background.
*/
const DEFAULT_STATE = {
appState: 'active'
};
ReducerRegistry.register(
'features/background',
(state = INITIAL_STATE, action) => {
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case _SET_APP_STATE_LISTENER:
return {

View File

@ -1,5 +1,6 @@
/**
* The type of redux action to set the Immersive change event listener.
* The type of (redux) action to set the react-native-immersive's change event
* listener.
*
* {
* type: _SET_IMMERSIVE_LISTENER,

View File

@ -3,13 +3,14 @@
import { _SET_IMMERSIVE_LISTENER } from './actionTypes';
/**
* Sets the listener to be used with React Native's Immersive API.
* Sets the change event listener to be used with react-native-immersive's API.
*
* @param {Function} listener - Function to be set as the change event listener.
* @param {Function} [listener] - The function to be used with
* react-native-immersive's API as the change event listener.
* @protected
* @returns {{
* type: _SET_IMMERSIVE_LISTENER,
* listener: Function
* listener: ?Function
* }}
*/
export function _setImmersiveListener(listener: ?Function) {

View File

@ -14,7 +14,7 @@ import {
import { Platform } from '../../base/react';
import { MiddlewareRegistry } from '../../base/redux';
import { _setImmersiveListener } from './actions';
import { _setImmersiveListener as _setImmersiveListenerA } from './actions';
import { _SET_IMMERSIVE_LISTENER } from './actionTypes';
/**
@ -28,59 +28,47 @@ import { _SET_IMMERSIVE_LISTENER } from './actionTypes';
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const result = next(action);
let fullScreen = null;
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case _SET_IMMERSIVE_LISTENER:
// XXX The React Native module Immersive is only implemented on Android
// and throws on other platforms.
if (Platform.OS === 'android') {
// Remove the current/old Immersive listener.
const { listener } = getState()['features/full-screen'];
listener && Immersive.removeImmersiveListener(listener);
// Add the new listener.
action.listener && Immersive.addImmersiveListener(action.listener);
}
break;
return _setImmersiveListenerF(store, next, action);
case APP_WILL_MOUNT: {
const context = {
dispatch,
getState
};
const result = next(action);
dispatch(
_setImmersiveListener(_onImmersiveChange.bind(undefined, context)));
break;
store.dispatch(
_setImmersiveListenerA(_onImmersiveChange.bind(undefined, store)));
return result;
}
case APP_WILL_UNMOUNT:
_setImmersiveListener(undefined);
store.dispatch(_setImmersiveListenerA(undefined));
break;
case CONFERENCE_WILL_JOIN:
case CONFERENCE_JOINED:
case SET_AUDIO_ONLY: {
const result = next(action);
const { audioOnly, conference, joining }
= getState()['features/base/conference'];
= store.getState()['features/base/conference'];
fullScreen = conference || joining ? !audioOnly : false;
break;
_setFullScreen(conference || joining ? !audioOnly : false);
return result;
}
case CONFERENCE_FAILED:
case CONFERENCE_LEFT:
fullScreen = false;
break;
case CONFERENCE_LEFT: {
const result = next(action);
_setFullScreen(false);
return result;
}
}
fullScreen !== null && _setFullScreen(fullScreen);
return result;
return next(action);
});
/**
@ -119,11 +107,43 @@ function _setFullScreen(fullScreen: boolean) {
// throws on other platforms.
if (Platform.OS === 'android') {
fullScreen ? Immersive.on() : Immersive.off();
} else {
// On platforms other than Android go with whatever React Native itself
// supports.
StatusBar.setHidden(fullScreen, 'slide');
}
}
return;
/**
* Notifies the feature filmstrip that the action
* {@link _SET_IMMERSIVE_LISTENER} is being dispatched within a specific redux
* store.
*
* @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.
* @private
* @returns {Object} The value returned by {@code next(action)}.
*/
function _setImmersiveListenerF({ getState }, next, action) {
// 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'];
if (oldListener !== newListener) {
oldListener && Immersive.removeImmersiveListener(oldListener);
newListener && Immersive.addImmersiveListener(newListener);
}
return result;
}
// On platforms other than Android go with whatever React Native itself
// supports.
StatusBar.setHidden(fullScreen, 'slide');
return next(action);
}

View File

@ -1,21 +1,17 @@
// @flow
import { ReducerRegistry } from '../../base/redux';
import { _SET_IMMERSIVE_LISTENER } from './actionTypes';
const INITIAL_STATE = {
listener: undefined
};
ReducerRegistry.register('features/full-screen', (state = {}, action) => {
switch (action.type) {
case _SET_IMMERSIVE_LISTENER:
return {
...state,
listener: action.listener
};
}
ReducerRegistry.register(
'features/full-screen',
(state = INITIAL_STATE, action) => {
switch (action.type) {
case _SET_IMMERSIVE_LISTENER:
return {
...state,
listener: action.listener
};
}
return state;
});
return state;
});

View File

@ -1,4 +1,4 @@
/* @flow */
// @flow
import { ReducerRegistry, set } from '../../base/redux';
@ -9,13 +9,13 @@ import {
} from './actionTypes';
/**
* The initial redux state of the feature network-activity.
* The default/initial redux state of the feature network-activity.
*
* @type {{
* requests: Map
* }}
*/
const _INITIAL_STATE = {
const DEFAULT_STATE = {
/**
* The ongoing network requests i.e. the network request which have been
* added to the redux store/state and have not been removed.
@ -27,7 +27,7 @@ const _INITIAL_STATE = {
ReducerRegistry.register(
'features/network-activity',
(state = _INITIAL_STATE, action) => {
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case _ADD_NETWORK_REQUEST: {
const {
@ -44,7 +44,7 @@ ReducerRegistry.register(
}
case _REMOVE_ALL_NETWORK_REQUESTS:
return set(state, 'requests', _INITIAL_STATE.requests);
return set(state, 'requests', DEFAULT_STATE.requests);
case _REMOVE_NETWORK_REQUEST: {
const { request: key } = action;