fix(Android|PiP): do not invoke 'enterPictureInPicture' in PAUSED state
Activity.enterPictureInPictureMode method must be invoked synchronously on userLeaveHint callback in order to be sure that the current Activity is still visible (does not transit to PAUSED state). Previously if the asynchronous processing would be delayed enough for the Activity to go into the PAUSED state it will be too late to go into the PiP mode.
This commit is contained in:
committed by
Saúl Ibarra Corretgé
parent
effd3728b6
commit
565fd37f28
@@ -9,16 +9,3 @@
|
||||
* @public
|
||||
*/
|
||||
export const ENTER_PICTURE_IN_PICTURE = Symbol('ENTER_PICTURE_IN_PICTURE');
|
||||
|
||||
/**
|
||||
* The type of redux action to set the {@code EventEmitter} subscriptions
|
||||
* utilized by the feature picture-in-picture.
|
||||
*
|
||||
* {
|
||||
* type: _SET_EMITTER_SUBSCRIPTIONS,
|
||||
* emitterSubscriptions: Array|undefined
|
||||
* }
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
export const _SET_EMITTER_SUBSCRIPTIONS = Symbol('_SET_EMITTER_SUBSCRIPTIONS');
|
||||
|
||||
@@ -4,10 +4,7 @@ import { NativeModules } from 'react-native';
|
||||
|
||||
import { Platform } from '../../base/react';
|
||||
|
||||
import {
|
||||
ENTER_PICTURE_IN_PICTURE,
|
||||
_SET_EMITTER_SUBSCRIPTIONS
|
||||
} from './actionTypes';
|
||||
import { ENTER_PICTURE_IN_PICTURE } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Enters (or rather initiates entering) picture-in-picture.
|
||||
@@ -47,22 +44,3 @@ export function enterPictureInPicture() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code EventEmitter} subscriptions utilized by the feature
|
||||
* picture-in-picture.
|
||||
*
|
||||
* @param {Array<Object>} emitterSubscriptions - The {@code EventEmitter}
|
||||
* subscriptions to be set.
|
||||
* @protected
|
||||
* @returns {{
|
||||
* type: _SET_EMITTER_SUBSCRIPTIONS,
|
||||
* emitterSubscriptions: Array<Object>
|
||||
* }}
|
||||
*/
|
||||
export function _setEmitterSubscriptions(emitterSubscriptions: ?Array<Object>) {
|
||||
return {
|
||||
type: _SET_EMITTER_SUBSCRIPTIONS,
|
||||
emitterSubscriptions
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './components';
|
||||
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { DeviceEventEmitter } from 'react-native';
|
||||
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../app';
|
||||
import { MiddlewareRegistry } from '../../base/redux';
|
||||
|
||||
import { enterPictureInPicture, _setEmitterSubscriptions } from './actions';
|
||||
import { _SET_EMITTER_SUBSCRIPTIONS } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Middleware that handles Picture-in-Picture requests. Currently it enters
|
||||
* the native PiP mode on Android, when requested.
|
||||
*
|
||||
* @param {Store} store - Redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case APP_WILL_MOUNT:
|
||||
return _appWillMount(store, next, action);
|
||||
|
||||
case APP_WILL_UNMOUNT:
|
||||
store.dispatch(_setEmitterSubscriptions(undefined));
|
||||
break;
|
||||
|
||||
case _SET_EMITTER_SUBSCRIPTIONS: {
|
||||
// Remove the current/old EventEmitter subscriptions.
|
||||
const { emitterSubscriptions } = store.getState()['features/pip'];
|
||||
|
||||
if (emitterSubscriptions) {
|
||||
for (const emitterSubscription of emitterSubscriptions) {
|
||||
// XXX We may be removing an EventEmitter subscription which is
|
||||
// in both the old and new Array of EventEmitter subscriptions!
|
||||
// Thankfully, we don't have such a practical use case at the
|
||||
// time of this writing.
|
||||
emitterSubscription.remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Notifies the feature pip that the action {@link APP_WILL_MOUNT} is being
|
||||
* dispatched within a specific redux {@code store}.
|
||||
*
|
||||
* @param {Store} store - The redux store in which the specified {@code action}
|
||||
* is being dispatched.
|
||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
||||
* specified {@code action} to the specified {@code store}.
|
||||
* @param {Action} action - The redux action {@code APP_WILL_MOUNT} which is
|
||||
* being dispatched in the specified {@code store}.
|
||||
* @private
|
||||
* @returns {*} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _appWillMount({ dispatch }, next, action) {
|
||||
dispatch(_setEmitterSubscriptions([
|
||||
|
||||
// Android's onUserLeaveHint activity lifecycle callback
|
||||
DeviceEventEmitter.addListener(
|
||||
'onUserLeaveHint',
|
||||
() => dispatch(enterPictureInPicture()))
|
||||
]));
|
||||
|
||||
return next(action);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { ReducerRegistry } from '../../base/redux';
|
||||
|
||||
import { _SET_EMITTER_SUBSCRIPTIONS } from './actionTypes';
|
||||
|
||||
ReducerRegistry.register('features/pip', (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
case _SET_EMITTER_SUBSCRIPTIONS:
|
||||
return {
|
||||
...state,
|
||||
emitterSubscriptions: action.emitterSubscriptions
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
});
|
||||
Reference in New Issue
Block a user