[RN] Alert the user when they need to manually grant a permission
This commit is contained in:
parent
9bca0e3b3d
commit
26f0f7f89c
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* Adapted from
|
||||||
|
* {@link https://github.com/Aleksandern/react-native-android-settings-library}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jitsi.meet.sdk;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
|
|
||||||
|
class AndroidSettingsModule extends ReactContextBaseJavaModule {
|
||||||
|
/**
|
||||||
|
* React Native module name.
|
||||||
|
*/
|
||||||
|
private static final String MODULE_NAME = "AndroidSettings";
|
||||||
|
|
||||||
|
public AndroidSettingsModule(ReactApplicationContext reactContext) {
|
||||||
|
super(reactContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return MODULE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void open() {
|
||||||
|
Context context = getReactApplicationContext();
|
||||||
|
Intent intent = new Intent();
|
||||||
|
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||||
|
intent.setData(
|
||||||
|
Uri.fromParts("package", context.getPackageName(), null));
|
||||||
|
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,6 +59,7 @@ public class JitsiMeetView extends FrameLayout {
|
||||||
private static List<NativeModule> createNativeModules(
|
private static List<NativeModule> createNativeModules(
|
||||||
ReactApplicationContext reactContext) {
|
ReactApplicationContext reactContext) {
|
||||||
return Arrays.<NativeModule>asList(
|
return Arrays.<NativeModule>asList(
|
||||||
|
new AndroidSettingsModule(reactContext),
|
||||||
new AudioModeModule(reactContext),
|
new AudioModeModule(reactContext),
|
||||||
new ExternalAPIModule(reactContext),
|
new ExternalAPIModule(reactContext),
|
||||||
new ProximityModule(reactContext)
|
new ProximityModule(reactContext)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import '../../mobile/audio-mode';
|
||||||
import '../../mobile/background';
|
import '../../mobile/background';
|
||||||
import '../../mobile/external-api';
|
import '../../mobile/external-api';
|
||||||
import '../../mobile/full-screen';
|
import '../../mobile/full-screen';
|
||||||
|
import '../../mobile/permissions';
|
||||||
import '../../mobile/proximity';
|
import '../../mobile/proximity';
|
||||||
import '../../mobile/wake-lock';
|
import '../../mobile/wake-lock';
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,17 @@
|
||||||
*/
|
*/
|
||||||
export const TRACK_ADDED = Symbol('TRACK_ADDED');
|
export const TRACK_ADDED = Symbol('TRACK_ADDED');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for when a track cannot be created because permissions have not been
|
||||||
|
* granted.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: TRACK_PERMISSION_ERROR,
|
||||||
|
* trackType: string
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export const TRACK_PERMISSION_ERROR = Symbol('TRACK_PERMISSION_ERROR');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action for when a track has been removed from the conference,
|
* Action for when a track has been removed from the conference,
|
||||||
* local or remote.
|
* local or remote.
|
||||||
|
|
|
@ -7,7 +7,12 @@ import {
|
||||||
} from '../media';
|
} from '../media';
|
||||||
import { getLocalParticipant } from '../participants';
|
import { getLocalParticipant } from '../participants';
|
||||||
|
|
||||||
import { TRACK_ADDED, TRACK_REMOVED, TRACK_UPDATED } from './actionTypes';
|
import {
|
||||||
|
TRACK_ADDED,
|
||||||
|
TRACK_PERMISSION_ERROR,
|
||||||
|
TRACK_REMOVED,
|
||||||
|
TRACK_UPDATED
|
||||||
|
} from './actionTypes';
|
||||||
import { createLocalTracksF } from './functions';
|
import { createLocalTracksF } from './functions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,14 +83,19 @@ export function createLocalTracksA(options = {}) {
|
||||||
},
|
},
|
||||||
/* firePermissionPromptIsShownEvent */ false,
|
/* firePermissionPromptIsShownEvent */ false,
|
||||||
store)
|
store)
|
||||||
.then(localTracks => dispatch(_updateLocalTracks(localTracks)));
|
.then(localTracks => dispatch(_updateLocalTracks(localTracks)))
|
||||||
|
.catch(({ gum }) => {
|
||||||
// TODO The function createLocalTracksF logs the rejection reason of
|
// If permissions are not allowed, alert the user.
|
||||||
// JitsiMeetJS.createLocalTracks so there is no real benefit to
|
if (gum
|
||||||
// logging it here as well. Technically though,
|
&& gum.error
|
||||||
// _updateLocalTracks may cause a rejection so it may be nice to log
|
&& gum.error.name === 'DOMException'
|
||||||
// it. It's not too big of a concern at the time of this writing
|
&& gum.error.message === 'NotAllowedError') {
|
||||||
// because React Native warns on unhandled Promise rejections.
|
dispatch({
|
||||||
|
type: TRACK_PERMISSION_ERROR,
|
||||||
|
trackType: device
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { Alert, Linking, NativeModules } from 'react-native';
|
||||||
|
|
||||||
|
import { Platform } from '../../base/react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows an alert panel which tells the user they have to manually grant some
|
||||||
|
* permissions by opening Settings. A button which opens Settings is provided.
|
||||||
|
*
|
||||||
|
* FIXME: translate.
|
||||||
|
*
|
||||||
|
* @param {string} trackType - Type of track that failed with a permission
|
||||||
|
* error.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function alertPermissionErrorWithSettings(trackType) {
|
||||||
|
const type = trackType === 'video' ? 'Camera' : 'Microphone';
|
||||||
|
|
||||||
|
Alert.alert(
|
||||||
|
'Permissions Error',
|
||||||
|
`${type} permission is required, please enable it in Settings.`,
|
||||||
|
[
|
||||||
|
{ text: 'Cancel' },
|
||||||
|
{
|
||||||
|
onPress: _openSettings,
|
||||||
|
text: 'Settings'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{ cancelable: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the settings panel for the current platform.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function _openSettings() {
|
||||||
|
switch (Platform.OS) {
|
||||||
|
case 'android':
|
||||||
|
NativeModules.AndroidSettings.open();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ios':
|
||||||
|
Linking.openURL('app-settings:');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
import './middleware';
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* @flow */
|
||||||
|
|
||||||
|
import { MiddlewareRegistry } from '../../base/redux';
|
||||||
|
import { TRACK_PERMISSION_ERROR } from '../../base/tracks';
|
||||||
|
|
||||||
|
import { alertPermissionErrorWithSettings } from './functions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Middleware that captures track permission errors and alerts the user so they
|
||||||
|
* can enable the permission themselves.
|
||||||
|
*
|
||||||
|
* @param {Store} store - The redux store.
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
MiddlewareRegistry.register(() => next => action => {
|
||||||
|
const result = next(action);
|
||||||
|
|
||||||
|
switch (action.type) {
|
||||||
|
case TRACK_PERMISSION_ERROR:
|
||||||
|
alertPermissionErrorWithSettings(action.trackType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
Loading…
Reference in New Issue