Преглед на файлове

[RN] Mute local video when app is in the background

j8
Saúl Ibarra Corretgé преди 8 години
родител
ревизия
4519f26adf

+ 1
- 0
react/features/app/components/App.native.js Целия файл

@@ -4,6 +4,7 @@ import { Linking } from 'react-native';
4 4
 
5 5
 import { Platform } from '../../base/react';
6 6
 import '../../audio-mode';
7
+import '../../background';
7 8
 import '../../full-screen';
8 9
 import '../../wake-lock';
9 10
 

+ 44
- 0
react/features/background/actionTypes.js Целия файл

@@ -0,0 +1,44 @@
1
+import { Symbol } from '../base/react';
2
+
3
+/**
4
+ * Action to set the AppState API change event listener.
5
+ *
6
+ * {
7
+ *      type: _SET_APP_STATE_LISTENER,
8
+ *      listener: Function
9
+ * }
10
+ *
11
+ * @private
12
+ */
13
+export const _SET_APP_STATE_LISTENER
14
+    = Symbol('_SET_APP_STATE_LISTENER');
15
+
16
+/**
17
+ * Action to signal video will be muted because the app is going to the
18
+ * background.
19
+ *
20
+ * {
21
+ *      type: _SET_BACKGROUND_VIDEO_MUTED,
22
+ *      muted: boolean
23
+ * }
24
+ *
25
+ * @private
26
+ */
27
+export const _SET_BACKGROUND_VIDEO_MUTED
28
+    = Symbol('_SET_BACKGROUND_VIDEO_MUTED');
29
+
30
+/**
31
+ * Action which signals that the App state has changed (in terms
32
+ * of execution mode).
33
+ *
34
+ * The application state can be one of 'active', 'inactive' or 'background',
35
+ * see: https://facebook.github.io/react-native/docs/appstate.html
36
+ *
37
+ * {
38
+ *      type: APP_STATE_CHANGED,
39
+ *      appState: string
40
+ * }
41
+ *
42
+ * @public
43
+ */
44
+export const APP_STATE_CHANGED = Symbol('APP_STATE_CHANGED');

+ 101
- 0
react/features/background/actions.js Целия файл

@@ -0,0 +1,101 @@
1
+import {
2
+    _SET_APP_STATE_LISTENER,
3
+    _SET_BACKGROUND_VIDEO_MUTED,
4
+    APP_STATE_CHANGED
5
+} from './actionTypes';
6
+import { setVideoMuted } from '../base/media';
7
+
8
+
9
+/**
10
+ * Signals that the App state has changed (in terms of execution mode). The
11
+ * application can be in 3 states: 'active', 'inactive' and 'background'.
12
+ *
13
+ * @see https://facebook.github.io/react-native/docs/appstate.html
14
+ *
15
+ * @param  {string} appState - The new App state.
16
+ * @returns {{
17
+ *      type: APP_STATE_CHANGED,
18
+ *      appState: string
19
+ * }}
20
+ */
21
+export function appStateChanged(appState: string) {
22
+    return {
23
+        type: APP_STATE_CHANGED,
24
+        appState
25
+    };
26
+}
27
+
28
+
29
+/**
30
+ * Signals that the app should mute video because it's now running in
31
+ * the background, or unmute it, if it came back from the background.
32
+ *
33
+ * If video was already muted nothing will happen, otherwise it will be
34
+ * muted. When coming back from the background the previous state will
35
+ * be restored.
36
+ *
37
+ * @param  {boolean} muted - Set to true if video should be muted, false
38
+ * otherwise.
39
+ * @returns {Function}
40
+ */
41
+export function setBackgroundVideoMuted(muted: boolean) {
42
+    return (dispatch, getState) => {
43
+        if (muted) {
44
+            const mediaState = getState()['features/base/media'];
45
+
46
+            if (mediaState.video.muted) {
47
+                // Video is already muted, do nothing.
48
+                return;
49
+            }
50
+        } else {
51
+            const bgState = getState()['features/background'];
52
+
53
+            if (!bgState.videoMuted) {
54
+                // We didn't mute video, do nothing.
55
+                return;
56
+            }
57
+        }
58
+
59
+        dispatch(_setBackgroundVideoMuted(muted));
60
+        dispatch(setVideoMuted(muted));
61
+    };
62
+}
63
+
64
+
65
+/**
66
+ * Internal action which sets the listener to be used with React Native's
67
+ * AppState API.
68
+ *
69
+ * @param  {Function} listener - Function to be set as the change event
70
+ * listener.
71
+ * @returns {{
72
+ *      type: _SET_APP_STATE_LISTENER,
73
+ *      listener: Function
74
+ * }}
75
+ */
76
+export function _setAppStateListener(listener: ?Function) {
77
+    return {
78
+        type: _SET_APP_STATE_LISTENER,
79
+        listener
80
+    };
81
+}
82
+
83
+
84
+/**
85
+ * Internal action which signals that video is going to be muted because the
86
+ * application is going to the background. This action is used to remember if
87
+ * video was muted due to the app going to the background vs user's choice.
88
+ *
89
+ * @param  {type} muted - Set to true if video will be muted, false otherwise.
90
+ * @private
91
+ * @returns {{
92
+ *      type: _SET_BACKGROUND_VIDEO_MUTED,
93
+ *      muted: boolean
94
+ * }}
95
+ */
96
+function _setBackgroundVideoMuted(muted: boolean) {
97
+    return {
98
+        type: _SET_BACKGROUND_VIDEO_MUTED,
99
+        muted
100
+    };
101
+}

+ 2
- 0
react/features/background/index.js Целия файл

@@ -0,0 +1,2 @@
1
+import './middleware';
2
+import './reducer';

+ 96
- 0
react/features/background/middleware.js Целия файл

@@ -0,0 +1,96 @@
1
+/* @flow */
2
+
3
+import { AppState } from 'react-native';
4
+import type { Dispatch } from 'redux';
5
+
6
+import {
7
+    _setAppStateListener,
8
+    appStateChanged,
9
+    setBackgroundVideoMuted
10
+} from './actions';
11
+import {
12
+    _SET_APP_STATE_LISTENER,
13
+    APP_STATE_CHANGED
14
+} from './actionTypes';
15
+
16
+import {
17
+    APP_WILL_MOUNT,
18
+    APP_WILL_UNMOUNT
19
+} from '../app';
20
+import { MiddlewareRegistry } from '../base/redux';
21
+
22
+
23
+/**
24
+ * Middleware that captures App lifetime actions and subscribes to application
25
+ * state changes. When the application state changes it will fire the action
26
+ * requred to mute or unmute the local video in case the application goes to
27
+ * the backgound or comes back from it.
28
+ *
29
+ * @see https://facebook.github.io/react-native/docs/appstate.html
30
+ * @param {Store} store - Redux store.
31
+ * @returns {Function}
32
+ */
33
+MiddlewareRegistry.register(store => next => action => {
34
+    switch (action.type) {
35
+    case _SET_APP_STATE_LISTENER: {
36
+        const bgState = store.getState()['features/background'];
37
+
38
+        if (bgState.appStateListener) {
39
+            AppState.removeEventListener('change', bgState.listener);
40
+        }
41
+        if (action.listener) {
42
+            AppState.addEventListener('change', action.listener);
43
+        }
44
+        break;
45
+    }
46
+    case APP_STATE_CHANGED:
47
+        _handleAppStateChange(store.dispatch, action.appState);
48
+        break;
49
+    case APP_WILL_MOUNT: {
50
+        const listener
51
+            = __onAppStateChanged.bind(undefined, store.dispatch);
52
+
53
+        store.dispatch(_setAppStateListener(listener));
54
+        break;
55
+    }
56
+    case APP_WILL_UNMOUNT:
57
+        store.dispatch(_setAppStateListener(null));
58
+        break;
59
+    }
60
+
61
+    return next(action);
62
+});
63
+
64
+
65
+/**
66
+ * Handler for app state changes. If will fire the necessary actions for
67
+ * local video to be muted when the app goes to the background, and to
68
+ * unmute it when it comes back.
69
+ *
70
+ * @param  {Dispatch} dispatch - Redux dispatch function.
71
+ * @param  {string} appState - Current app state.
72
+ * @private
73
+ * @returns {void}
74
+ */
75
+function _handleAppStateChange(dispatch: Dispatch<*>, appState: string) {
76
+    // XXX: we purposely don't handle the 'inactive' state.
77
+    if (appState === 'background') {
78
+        dispatch(setBackgroundVideoMuted(true));
79
+    } else if (appState === 'active') {
80
+        dispatch(setBackgroundVideoMuted(false));
81
+    }
82
+}
83
+
84
+
85
+/**
86
+ * Handler called by React's AppState API indicating that the application state
87
+ * has changed.
88
+ *
89
+ * @param  {Dispatch} dispatch - Redux dispatch function.
90
+ * @param  {string} appState - The current application execution state.
91
+ * @private
92
+ * @returns {void}
93
+ */
94
+function __onAppStateChanged(dispatch: Dispatch<*>, appState: string) {
95
+    dispatch(appStateChanged(appState));
96
+}

+ 31
- 0
react/features/background/reducer.js Целия файл

@@ -0,0 +1,31 @@
1
+import {
2
+    _SET_APP_STATE_LISTENER,
3
+    _SET_BACKGROUND_VIDEO_MUTED,
4
+    APP_STATE_CHANGED
5
+} from './actionTypes';
6
+import { ReducerRegistry } from '../base/redux';
7
+
8
+
9
+ReducerRegistry.register('features/background', (state = {}, action) => {
10
+    switch (action.type) {
11
+    case _SET_APP_STATE_LISTENER:
12
+        return {
13
+            ...state,
14
+            appStateListener: action.listener
15
+        };
16
+
17
+    case _SET_BACKGROUND_VIDEO_MUTED:
18
+        return {
19
+            ...state,
20
+            videoMuted: action.muted
21
+        };
22
+
23
+    case APP_STATE_CHANGED:
24
+        return {
25
+            ...state,
26
+            appState: action.appState
27
+        };
28
+    }
29
+
30
+    return state;
31
+});

Loading…
Отказ
Запис