mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
feat: Permissions API
This commit is contained in:
8
packages/core/permissions/common.ts
Normal file
8
packages/core/permissions/common.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export enum PermissionStatus {
|
||||
authorized = 'authorized',
|
||||
denied = 'denied',
|
||||
limited = 'limited',
|
||||
restricted = 'restricted',
|
||||
undetermined = 'undetermined',
|
||||
never_ask_again = 'never_ask_again',
|
||||
}
|
||||
419
packages/core/permissions/index.android.ts
Normal file
419
packages/core/permissions/index.android.ts
Normal file
@@ -0,0 +1,419 @@
|
||||
import { Trace } from '../trace';
|
||||
import { AndroidActivityRequestPermissionsEventData, AndroidApplication, android as androidApp } from '../application';
|
||||
import { getBoolean, setBoolean } from '../application-settings';
|
||||
import { PermissionStatus } from './common';
|
||||
import { PermissionCheckOptions, PermissionsType, PermissionRationale, PermissionRequestOptions } from '.';
|
||||
|
||||
export * from './common';
|
||||
|
||||
let ANDROID_SDK = -1;
|
||||
function getAndroidSDK() {
|
||||
if (ANDROID_SDK === -1) {
|
||||
ANDROID_SDK = android.os.Build.VERSION.SDK_INT;
|
||||
}
|
||||
return ANDROID_SDK;
|
||||
}
|
||||
|
||||
const MARSHMALLOW = 23;
|
||||
const ANDROIDQ = 29;
|
||||
const ANDROIDS = 31;
|
||||
|
||||
const NativePermissionsTypes: PermissionsType[] = ['location', 'camera', 'mediaLocation', 'microphone', 'contacts', 'event', 'storage', 'photo', 'callPhone', 'readSms', 'receiveSms', 'bluetoothScan', 'bluetoothConnect', 'bluetooth'];
|
||||
type NativePermissionsNames = typeof NativePermissionsTypes; // type Names = readonly ['Mike', 'Jeff', 'Ben']
|
||||
type NativePermissions = NativePermissionsNames[number];
|
||||
function getNativePermissions(permission: NativePermissions, options?) {
|
||||
switch (permission) {
|
||||
case 'location': {
|
||||
const result = [android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION];
|
||||
if (getAndroidSDK() >= ANDROIDQ) {
|
||||
const type = typeof options === 'string' ? options : options && options.type;
|
||||
if (type === 'always') {
|
||||
result.push(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case 'camera': {
|
||||
return [android.Manifest.permission.CAMERA];
|
||||
}
|
||||
case 'mediaLocation': {
|
||||
if (getAndroidSDK() >= ANDROIDQ) {
|
||||
return [android.Manifest.permission.ACCESS_MEDIA_LOCATION];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'microphone': {
|
||||
return [android.Manifest.permission.RECORD_AUDIO];
|
||||
}
|
||||
case 'contacts': {
|
||||
return [android.Manifest.permission.READ_CONTACTS];
|
||||
}
|
||||
case 'event': {
|
||||
return [android.Manifest.permission.READ_CALENDAR];
|
||||
}
|
||||
case 'storage': {
|
||||
return [android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE];
|
||||
}
|
||||
case 'photo': {
|
||||
return [android.Manifest.permission.WRITE_EXTERNAL_STORAGE];
|
||||
}
|
||||
case 'callPhone': {
|
||||
return [android.Manifest.permission.CALL_PHONE];
|
||||
}
|
||||
case 'readSms': {
|
||||
return [android.Manifest.permission.READ_SMS];
|
||||
}
|
||||
case 'receiveSms': {
|
||||
return [android.Manifest.permission.RECEIVE_SMS];
|
||||
}
|
||||
case 'bluetoothScan': {
|
||||
if (getAndroidSDK() >= ANDROIDS) {
|
||||
return [android.Manifest.permission.BLUETOOTH_SCAN];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'bluetoothConnect': {
|
||||
if (getAndroidSDK() >= ANDROIDS) {
|
||||
return [android.Manifest.permission.BLUETOOTH_CONNECT];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'bluetooth': {
|
||||
if (getAndroidSDK() >= ANDROIDS) {
|
||||
return [android.Manifest.permission.BLUETOOTH_ADVERTISE];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
const STORAGE_KEY = '@NSPermissions:didAskPermission:';
|
||||
|
||||
const setDidAskOnce = (permission: string) => Promise.resolve().then(() => setBoolean(STORAGE_KEY + permission, true));
|
||||
|
||||
const getDidAskOnce = (permission: string) => Promise.resolve(!!getBoolean(STORAGE_KEY + permission));
|
||||
|
||||
namespace PermissionsAndroid {
|
||||
/**
|
||||
* A list of specified "dangerous" permissions that require prompting the user
|
||||
*/
|
||||
// export const PERMISSIONS = {
|
||||
// READ_CALENDAR: 'android.permission.READ_CALENDAR',
|
||||
// WRITE_CALENDAR: 'android.permission.WRITE_CALENDAR',
|
||||
// CAMERA: 'android.permission.CAMERA',
|
||||
// READ_CONTACTS: 'android.permission.READ_CONTACTS',
|
||||
// WRITE_CONTACTS: 'android.permission.WRITE_CONTACTS',
|
||||
// GET_ACCOUNTS: 'android.permission.GET_ACCOUNTS',
|
||||
// ACCESS_FINE_LOCATION: 'android.permission.ACCESS_FINE_LOCATION',
|
||||
// ACCESS_COARSE_LOCATION: 'android.permission.ACCESS_COARSE_LOCATION',
|
||||
// RECORD_AUDIO: 'android.permission.RECORD_AUDIO',
|
||||
// READ_PHONE_STATE: 'android.permission.READ_PHONE_STATE',
|
||||
// CALL_PHONE: 'android.permission.CALL_PHONE',
|
||||
// READ_CALL_LOG: 'android.permission.READ_CALL_LOG',
|
||||
// WRITE_CALL_LOG: 'android.permission.WRITE_CALL_LOG',
|
||||
// ADD_VOICEMAIL: 'com.android.voicemail.permission.ADD_VOICEMAIL',
|
||||
// USE_SIP: 'android.permission.USE_SIP',
|
||||
// PROCESS_OUTGOING_CALLS: 'android.permission.PROCESS_OUTGOING_CALLS',
|
||||
// BODY_SENSORS: 'android.permission.BODY_SENSORS',
|
||||
// SEND_SMS: 'android.permission.SEND_SMS',
|
||||
// RECEIVE_SMS: 'android.permission.RECEIVE_SMS',
|
||||
// READ_SMS: 'android.permission.READ_SMS',
|
||||
// RECEIVE_WAP_PUSH: 'android.permission.RECEIVE_WAP_PUSH',
|
||||
// RECEIVE_MMS: 'android.permission.RECEIVE_MMS',
|
||||
// READ_EXTERNAL_STORAGE: 'android.permission.READ_EXTERNAL_STORAGE',
|
||||
// WRITE_EXTERNAL_STORAGE: 'android.permission.WRITE_EXTERNAL_STORAGE'
|
||||
// };
|
||||
|
||||
export const RESULTS: { GRANTED: PermissionStatus; DENIED: PermissionStatus; NEVER_ASK_AGAIN: PermissionStatus } = {
|
||||
GRANTED: PermissionStatus.authorized,
|
||||
DENIED: PermissionStatus.denied,
|
||||
NEVER_ASK_AGAIN: PermissionStatus.never_ask_again,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a promise resolving to a boolean value as to whether the specified
|
||||
* permissions has been granted
|
||||
*
|
||||
* See https://facebook.github.io/react-native/docs/permissionsandroid.html#check
|
||||
*/
|
||||
export async function check(permission: string | string[]) {
|
||||
const context: android.content.Context = androidApp.foregroundActivity || androidApp.startActivity;
|
||||
let result = true;
|
||||
const granted = android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
if (!Array.isArray(permission)) {
|
||||
permission = [permission];
|
||||
}
|
||||
if (getAndroidSDK() < MARSHMALLOW) {
|
||||
permission.forEach((p) => (result = result && context.checkPermission(p, android.os.Process.myPid(), android.os.Process.myUid()) === granted));
|
||||
} else {
|
||||
permission.forEach((p) => (result = result && context.checkSelfPermission(p) === granted));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user to enable a permission and returns a promise resolving to a
|
||||
* string value indicating whether the user allowed or denied the request
|
||||
*
|
||||
* See https://facebook.github.io/react-native/docs/permissionsandroid.html#request
|
||||
*/
|
||||
export async function request(permission: string, rationale?: PermissionRationale): Promise<PermissionStatus> {
|
||||
// if (rationale) {
|
||||
// const shouldShowRationale = await shouldShowRequestPermissionRationale(permission);
|
||||
|
||||
// if (shouldShowRationale) {
|
||||
// return new Promise((resolve, reject) => {
|
||||
|
||||
// NativeModules.DialogManagerAndroid.showAlert(rationale, () => reject(new Error('Error showing rationale')), () => resolve(requestPermission(permission)));
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
return requestPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user to enable multiple permissions in the same dialog and
|
||||
* returns an object with the permissions as keys and strings as values
|
||||
* indicating whether the user allowed or denied the request
|
||||
*
|
||||
* See https://facebook.github.io/react-native/docs/permissionsandroid.html#requestmultiple
|
||||
*/
|
||||
export function requestMultiple(permissions: string[]): Promise<{ [permission: string]: PermissionStatus }> {
|
||||
return requestMultiplePermissions(permissions);
|
||||
}
|
||||
}
|
||||
|
||||
// PermissionsAndroid = new PermissionsAndroid();
|
||||
|
||||
let mRequestCode = 0;
|
||||
function requestPermission(permission: string): Promise<PermissionStatus> {
|
||||
const activity: android.app.Activity = androidApp.foregroundActivity || androidApp.startActivity;
|
||||
if (getAndroidSDK() < MARSHMALLOW) {
|
||||
return Promise.resolve(activity.checkPermission(permission, android.os.Process.myPid(), android.os.Process.myUid()) === android.content.pm.PackageManager.PERMISSION_GRANTED ? PermissionsAndroid.RESULTS.GRANTED : PermissionsAndroid.RESULTS.DENIED);
|
||||
}
|
||||
if (activity.checkSelfPermission(permission) === android.content.pm.PackageManager.PERMISSION_GRANTED) {
|
||||
return Promise.resolve(PermissionsAndroid.RESULTS.GRANTED);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const requestCode = mRequestCode++;
|
||||
activity.requestPermissions([permission], requestCode);
|
||||
androidApp.on(AndroidApplication.activityRequestPermissionsEvent, (args: AndroidActivityRequestPermissionsEventData) => {
|
||||
if (args.requestCode === requestCode) {
|
||||
if (args.grantResults.length > 0) {
|
||||
if (args.grantResults.length > 0 && args.grantResults[0] === android.content.pm.PackageManager.PERMISSION_GRANTED) {
|
||||
resolve(PermissionsAndroid.RESULTS.GRANTED);
|
||||
} else {
|
||||
if (activity.shouldShowRequestPermissionRationale(permission)) {
|
||||
resolve(PermissionsAndroid.RESULTS.DENIED);
|
||||
} else {
|
||||
resolve(PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// it is possible that the permissions request interaction with the user is interrupted. In this case you will receive empty permissions and results arrays which should be treated as a cancellation.
|
||||
reject();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function requestMultiplePermissions(permissions: string[]): Promise<{ [permission: string]: PermissionStatus }> {
|
||||
const grantedPermissions = {};
|
||||
const permissionsToCheck = [];
|
||||
let checkedPermissionsCount = 0;
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`requestMultiplePermissions ${permissions}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
const context: android.content.Context = androidApp.foregroundActivity || androidApp.startActivity;
|
||||
|
||||
for (let i = 0; i < permissions.length; i++) {
|
||||
const perm = permissions[i];
|
||||
|
||||
if (getAndroidSDK() < MARSHMALLOW) {
|
||||
grantedPermissions[perm] = context.checkPermission(perm, android.os.Process.myPid(), android.os.Process.myUid()) === android.content.pm.PackageManager.PERMISSION_GRANTED ? PermissionsAndroid.RESULTS.GRANTED : PermissionsAndroid.RESULTS.DENIED;
|
||||
checkedPermissionsCount++;
|
||||
} else if (context.checkSelfPermission(perm) === android.content.pm.PackageManager.PERMISSION_GRANTED) {
|
||||
grantedPermissions[perm] = PermissionsAndroid.RESULTS.GRANTED;
|
||||
checkedPermissionsCount++;
|
||||
} else {
|
||||
permissionsToCheck.push(perm);
|
||||
}
|
||||
}
|
||||
if (permissions.length === checkedPermissionsCount) {
|
||||
return grantedPermissions;
|
||||
}
|
||||
const activity: android.app.Activity = androidApp.foregroundActivity || androidApp.startActivity;
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const requestCode = mRequestCode++;
|
||||
|
||||
activity.requestPermissions(permissionsToCheck, requestCode);
|
||||
androidApp.on(AndroidApplication.activityRequestPermissionsEvent, (args: AndroidActivityRequestPermissionsEventData) => {
|
||||
if (args.requestCode === requestCode) {
|
||||
const results = args.grantResults;
|
||||
for (let j = 0; j < permissionsToCheck.length; j++) {
|
||||
const permission = permissionsToCheck[j];
|
||||
if (results.length > j && results[j] === android.content.pm.PackageManager.PERMISSION_GRANTED) {
|
||||
grantedPermissions[permission] = PermissionsAndroid.RESULTS.GRANTED;
|
||||
} else {
|
||||
if (activity.shouldShowRequestPermissionRationale(permission)) {
|
||||
grantedPermissions[permission] = PermissionsAndroid.RESULTS.DENIED;
|
||||
} else {
|
||||
grantedPermissions[permission] = PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
resolve(grantedPermissions);
|
||||
}
|
||||
|
||||
// if (args.grantResults.length > 0 && args.grantResults[0] === android.content.pm.PackageManager.PERMISSION_GRANTED) {
|
||||
// resolve(PermissionStatus.GRANTED);
|
||||
// } else {
|
||||
// if (activity.shouldShowRequestPermissionRationale(permission)) {
|
||||
// resolve(PermissionStatus.DENIED);
|
||||
// } else {
|
||||
// resolve(PermissionStatus.NEVER_ASK_AGAIN);
|
||||
// }
|
||||
// }
|
||||
});
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function shouldShowRequestPermissionRationale(permission: string | string[]) {
|
||||
if (getAndroidSDK() < MARSHMALLOW) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
const activity: android.app.Activity = androidApp.foregroundActivity || androidApp.startActivity;
|
||||
try {
|
||||
if (Array.isArray(permission)) {
|
||||
return Promise.resolve(permission.reduce((accu, p) => accu && activity.shouldShowRequestPermissionRationale(p), true));
|
||||
}
|
||||
return Promise.resolve(activity.shouldShowRequestPermissionRationale(permission));
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
export class Permissions {
|
||||
static canOpenSettings() {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
static openSettings() {
|
||||
const activity = androidApp.foregroundActivity || androidApp.startActivity;
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const onActivityResultHandler = (data) => {
|
||||
if (data.requestCode === 5140) {
|
||||
androidApp.off(AndroidApplication.activityResultEvent, onActivityResultHandler);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
androidApp.on(AndroidApplication.activityResultEvent, onActivityResultHandler);
|
||||
const intent = new android.content.Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
intent.setData(android.net.Uri.parse('package:' + activity.getPackageName()));
|
||||
activity.startActivityForResult(intent, 5140);
|
||||
});
|
||||
}
|
||||
|
||||
static getTypes() {
|
||||
return NativePermissionsTypes;
|
||||
}
|
||||
|
||||
static async check(permission: PermissionsType, options?: PermissionCheckOptions): Promise<[PermissionStatus, boolean]> {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`check ${permission}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
const perms: string | string[] = getNativePermissions(permission, options);
|
||||
if (!perms) {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`${permission} is not a valid permission type on Android version`, Trace.categories.Permissions, Trace.messageType.warn);
|
||||
}
|
||||
return [PermissionStatus.authorized, true];
|
||||
}
|
||||
|
||||
const isAuthorized = await PermissionsAndroid.check(perms);
|
||||
if (isAuthorized) {
|
||||
if (getAndroidSDK() >= ANDROIDQ && permission === 'location') {
|
||||
const type = typeof options === 'string' ? options : options && options.type;
|
||||
if (type === 'always') {
|
||||
const backAuthorized = await PermissionsAndroid.check(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION);
|
||||
return [PermissionStatus.authorized, backAuthorized];
|
||||
}
|
||||
}
|
||||
return [PermissionStatus.authorized, true];
|
||||
}
|
||||
|
||||
return getDidAskOnce(permission).then((didAsk) => {
|
||||
if (didAsk) {
|
||||
return shouldShowRequestPermissionRationale(perms).then((shouldShow) => [shouldShow ? PermissionStatus.denied : PermissionStatus.restricted, true]);
|
||||
}
|
||||
|
||||
return [PermissionStatus.undetermined, true];
|
||||
});
|
||||
}
|
||||
|
||||
static request(permission: PermissionsType | PermissionsType[] | string[], options?: PermissionRequestOptions): Promise<[PermissionStatus, boolean] | { [permission: string]: PermissionStatus }> {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`request ${permission}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
let types: string[] = [];
|
||||
if (Array.isArray(permission)) {
|
||||
permission.forEach((s) => {
|
||||
if (s.startsWith('android.permission.')) {
|
||||
types.push(s);
|
||||
} else {
|
||||
types.push(...getNativePermissions(s as PermissionsType, options));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (permission.startsWith('android.permission.')) {
|
||||
types.push(permission);
|
||||
} else {
|
||||
types = getNativePermissions(permission, options);
|
||||
}
|
||||
}
|
||||
if (types.length === 0) {
|
||||
return Promise.resolve([PermissionStatus.authorized, true]);
|
||||
}
|
||||
|
||||
const rationale = typeof options === 'string' ? undefined : options && options.rationale;
|
||||
if (types.length > 1) {
|
||||
return requestMultiplePermissions(types);
|
||||
}
|
||||
return PermissionsAndroid.request(types[0], rationale).then((result) => {
|
||||
// PermissionsAndroid.request() to native module resolves to boolean
|
||||
// rather than string if running on OS version prior to Android M
|
||||
if (typeof result === 'boolean') {
|
||||
return [result ? PermissionStatus.authorized : PermissionStatus.denied, true];
|
||||
}
|
||||
|
||||
if (Array.isArray(permission)) {
|
||||
return Promise.all(permission.map(setDidAskOnce)).then(() => [result as PermissionStatus, true]);
|
||||
}
|
||||
return setDidAskOnce(permission).then(() => [result as PermissionStatus, true]);
|
||||
});
|
||||
}
|
||||
|
||||
static checkMultiple(permissions: PermissionsType[]) {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`checkMultiple ${permissions}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
return Promise.all(permissions.map((permission) => this.check(permission))).then((result) =>
|
||||
result.reduce((acc, value, index) => {
|
||||
const name = permissions[index];
|
||||
acc[name] = value;
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
}
|
||||
}
|
||||
21
packages/core/permissions/index.d.ts
vendored
Normal file
21
packages/core/permissions/index.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
export * from './common';
|
||||
export type PermissionsType = 'location' | 'camera' | 'microphone' | 'photo' | 'contacts' | 'event' | 'reminder' | 'bluetooth' | 'bluetoothConnect' | 'bluetoothScan' | 'notification' | 'backgroundRefresh' | 'speechRecognition' | 'mediaLocation' | 'mediaLibrary' | 'motion' | 'storage' | 'callPhone' | 'readSms' | 'receiveSms';
|
||||
export interface PermissionRationale {
|
||||
title: string;
|
||||
message: string;
|
||||
buttonPositive?: string;
|
||||
buttonNegative?: string;
|
||||
buttonNeutral?: string;
|
||||
}
|
||||
export type PermissionCheckOptions = string | { type: string };
|
||||
export type PermissionRequestOptions = string | { type: string; rationale?: PermissionRationale };
|
||||
export type PermissionResult<T> = T extends any[] ? { [k: string]: Status } : [Status, boolean];
|
||||
|
||||
export class Permissions {
|
||||
static canOpenSettings(): Promise<boolean>;
|
||||
static openSettings(): Promise<boolean>;
|
||||
static getTypes(): Permissions[];
|
||||
static check<T = Permissions>(permission: T, options?: CheckOptions): Promise<PermissionResult<T>>;
|
||||
static request<T = Permissions | Permissions[] | string[]>(permission: T, options?: RequestOptions): Promise<PermissionResult<T>>;
|
||||
static checkMultiple<T = Permissions[]>(permissions: T): Promise<PermissionResult<T>>;
|
||||
}
|
||||
788
packages/core/permissions/index.ios.ts
Normal file
788
packages/core/permissions/index.ios.ts
Normal file
@@ -0,0 +1,788 @@
|
||||
import { Device } from '../platform';
|
||||
import { Trace } from '../trace';
|
||||
import { PermissionStatus } from './common';
|
||||
import { PermissionCheckOptions, PermissionsType, PermissionRequestOptions } from '.';
|
||||
|
||||
export * from './common';
|
||||
|
||||
export namespace PermissionsIOS {
|
||||
namespace NSPLocation {
|
||||
let status: PermissionStatus = PermissionStatus.undetermined;
|
||||
function getStatusFromCLAuthorizationStatus(lStatus: CLAuthorizationStatus, type?: string): [PermissionStatus, boolean] {
|
||||
let always = false;
|
||||
switch (lStatus) {
|
||||
case CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedAlways:
|
||||
always = true;
|
||||
status = PermissionStatus.authorized;
|
||||
break;
|
||||
case CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedWhenInUse:
|
||||
status = PermissionStatus.authorized;
|
||||
break;
|
||||
case CLAuthorizationStatus.kCLAuthorizationStatusDenied:
|
||||
status = PermissionStatus.denied;
|
||||
break;
|
||||
case CLAuthorizationStatus.kCLAuthorizationStatusRestricted:
|
||||
status = PermissionStatus.restricted;
|
||||
break;
|
||||
default:
|
||||
status = PermissionStatus.undetermined;
|
||||
}
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`NSPLocation getStatusFromCLAuthorizationStatus ${status}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
return [status, always];
|
||||
}
|
||||
export function getStatusForType(type?: string): [PermissionStatus, boolean] {
|
||||
const status2 = CLLocationManager.authorizationStatus();
|
||||
return getStatusFromCLAuthorizationStatus(status2, type);
|
||||
}
|
||||
let locationManager: CLLocationManager;
|
||||
let locationManagerDelegate: CLLocationManagerDelegateImpl;
|
||||
export type SubCLLocationManagerDelegate = Partial<CLLocationManagerDelegate>;
|
||||
@NativeClass
|
||||
export class CLLocationManagerDelegateImpl extends NSObject implements CLLocationManagerDelegate {
|
||||
public static ObjCProtocols = [CLLocationManagerDelegate];
|
||||
|
||||
private subDelegates: SubCLLocationManagerDelegate[];
|
||||
|
||||
public addSubDelegate(delegate: SubCLLocationManagerDelegate) {
|
||||
if (!this.subDelegates) {
|
||||
this.subDelegates = [];
|
||||
}
|
||||
const index = this.subDelegates.indexOf(delegate);
|
||||
if (index === -1) {
|
||||
this.subDelegates.push(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
public removeSubDelegate(delegate: SubCLLocationManagerDelegate) {
|
||||
const index = this.subDelegates.indexOf(delegate);
|
||||
if (index !== -1) {
|
||||
this.subDelegates.splice(index, 1);
|
||||
}
|
||||
}
|
||||
static new(): CLLocationManagerDelegateImpl {
|
||||
return super.new() as CLLocationManagerDelegateImpl;
|
||||
}
|
||||
public initDelegate() {
|
||||
this.subDelegates = [];
|
||||
return this;
|
||||
}
|
||||
locationManagerDidChangeAuthorizationStatus(manager: CLLocationManager, status: CLAuthorizationStatus) {
|
||||
this.subDelegates &&
|
||||
this.subDelegates.forEach((d) => {
|
||||
if (d.locationManagerDidChangeAuthorizationStatus) {
|
||||
d.locationManagerDidChangeAuthorizationStatus(manager, status);
|
||||
}
|
||||
});
|
||||
}
|
||||
// locationManagerDidFailWithError(manager: CLLocationManager, error: NSError) {
|
||||
// this.subDelegates &&
|
||||
// this.subDelegates.forEach(d => {
|
||||
// if (d.locationManagerDidFailWithError) {
|
||||
// d.locationManagerDidFailWithError(manager, error);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
export function request(type): Promise<[PermissionStatus, boolean]> {
|
||||
const status = getStatusForType(type);
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`NSPLocation request ${type}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
if (status[0] === PermissionStatus.undetermined) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!locationManager) {
|
||||
locationManager = CLLocationManager.new();
|
||||
}
|
||||
if (!locationManagerDelegate) {
|
||||
locationManagerDelegate = CLLocationManagerDelegateImpl.new().initDelegate();
|
||||
locationManager.delegate = locationManagerDelegate;
|
||||
}
|
||||
const subD = {
|
||||
locationManagerDidChangeAuthorizationStatus: (manager, status: CLAuthorizationStatus) => {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`locationManagerDidChangeAuthorizationStatus ${status}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
if (status !== CLAuthorizationStatus.kCLAuthorizationStatusNotDetermined) {
|
||||
if (locationManagerDelegate) {
|
||||
locationManagerDelegate.removeSubDelegate(subD);
|
||||
locationManagerDelegate = null;
|
||||
}
|
||||
if (locationManager) {
|
||||
locationManager.delegate = null;
|
||||
locationManager = null;
|
||||
}
|
||||
const rStatus = getStatusFromCLAuthorizationStatus(status, type);
|
||||
resolve(rStatus);
|
||||
// } else {
|
||||
// reject('kCLAuthorizationStatusNotDetermined');
|
||||
}
|
||||
},
|
||||
};
|
||||
locationManagerDelegate.addSubDelegate(subD);
|
||||
try {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`NSPLocation requestAuthorization ${type}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
if (type === 'always') {
|
||||
locationManager.requestAlwaysAuthorization();
|
||||
} else {
|
||||
locationManager.requestWhenInUseAuthorization();
|
||||
}
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
if (locationManagerDelegate) {
|
||||
locationManagerDelegate.removeSubDelegate(subD);
|
||||
locationManagerDelegate = null;
|
||||
}
|
||||
if (locationManager) {
|
||||
locationManager.delegate = null;
|
||||
locationManager = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// if (CLLocationManager.authorizationStatus() === CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedWhenInUse && type === 'always') {
|
||||
// return Promise.resolve(PermissionStatus.denied);
|
||||
// } else {
|
||||
return Promise.resolve(status);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace NSPBluetooth {
|
||||
let status: PermissionStatus = PermissionStatus.undetermined;
|
||||
export function getStatus(): [PermissionStatus, boolean] {
|
||||
const status2 = CBPeripheralManager.authorizationStatus();
|
||||
switch (status2) {
|
||||
case CBPeripheralManagerAuthorizationStatus.Authorized:
|
||||
status = PermissionStatus.authorized;
|
||||
break;
|
||||
case CBPeripheralManagerAuthorizationStatus.Denied:
|
||||
status = PermissionStatus.denied;
|
||||
break;
|
||||
case CBPeripheralManagerAuthorizationStatus.Restricted:
|
||||
status = PermissionStatus.restricted;
|
||||
break;
|
||||
default:
|
||||
status = PermissionStatus.undetermined;
|
||||
}
|
||||
return [status, true];
|
||||
}
|
||||
export type SubCBPeripheralManagerDelegate = Partial<CBPeripheralManagerDelegate>;
|
||||
@NativeClass
|
||||
export class CBPeripheralManagerDelegateImpl extends NSObject implements CBPeripheralManagerDelegate {
|
||||
public static ObjCProtocols = [CBPeripheralManagerDelegate];
|
||||
|
||||
private subDelegates: SubCBPeripheralManagerDelegate[];
|
||||
|
||||
public addSubDelegate(delegate: SubCBPeripheralManagerDelegate) {
|
||||
const index = this.subDelegates.indexOf(delegate);
|
||||
if (index === -1) {
|
||||
this.subDelegates.push(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
public removeSubDelegate(delegate: SubCBPeripheralManagerDelegate) {
|
||||
const index = this.subDelegates.indexOf(delegate);
|
||||
if (index !== -1) {
|
||||
this.subDelegates.splice(index, 1);
|
||||
}
|
||||
}
|
||||
static new(): CBPeripheralManagerDelegateImpl {
|
||||
return super.new() as CBPeripheralManagerDelegateImpl;
|
||||
}
|
||||
public initDelegate(): CBPeripheralManagerDelegateImpl {
|
||||
this.subDelegates = [];
|
||||
return this;
|
||||
}
|
||||
peripheralManagerDidUpdateState(peripheralManager) {
|
||||
this.subDelegates.forEach((d) => {
|
||||
if (d.peripheralManagerDidUpdateState) {
|
||||
d.peripheralManagerDidUpdateState(peripheralManager);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
let peripheralManager: CBPeripheralManager;
|
||||
export function request(): Promise<[PermissionStatus, boolean]> {
|
||||
const status = getStatus();
|
||||
if (status[0] === PermissionStatus.undetermined) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!peripheralManager) {
|
||||
peripheralManager = CBPeripheralManager.new();
|
||||
peripheralManager.delegate = CBPeripheralManagerDelegateImpl.new().initDelegate();
|
||||
}
|
||||
const subD = {
|
||||
peripheralManagerDidUpdateState: (peripheralManager) => {
|
||||
if (peripheralManager) {
|
||||
peripheralManager.stopAdvertising();
|
||||
(peripheralManager.delegate as CBPeripheralManagerDelegateImpl).removeSubDelegate(subD);
|
||||
peripheralManager.delegate = null;
|
||||
peripheralManager = null;
|
||||
}
|
||||
// for some reason, checking permission right away returns denied. need to wait a tiny bit
|
||||
setTimeout(() => {
|
||||
resolve(getStatus());
|
||||
}, 100);
|
||||
},
|
||||
};
|
||||
(peripheralManager.delegate as CBPeripheralManagerDelegateImpl).addSubDelegate(subD);
|
||||
try {
|
||||
peripheralManager.startAdvertising(null);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace NSPAudioVideo {
|
||||
let status: PermissionStatus = PermissionStatus.undetermined;
|
||||
function typeFromString(value: string) {
|
||||
if (value === 'audio') {
|
||||
return AVMediaTypeAudio;
|
||||
} else {
|
||||
return AVMediaTypeVideo;
|
||||
}
|
||||
}
|
||||
export function getStatus(type?: string): [PermissionStatus, boolean] {
|
||||
const videoStatus = AVCaptureDevice.authorizationStatusForMediaType(typeFromString(type));
|
||||
switch (videoStatus) {
|
||||
case AVAuthorizationStatus.Authorized:
|
||||
status = PermissionStatus.authorized;
|
||||
break;
|
||||
case AVAuthorizationStatus.Denied:
|
||||
status = PermissionStatus.denied;
|
||||
break;
|
||||
case AVAuthorizationStatus.Restricted:
|
||||
status = PermissionStatus.restricted;
|
||||
break;
|
||||
default:
|
||||
status = PermissionStatus.undetermined;
|
||||
}
|
||||
return [status, true];
|
||||
}
|
||||
|
||||
export function request(type): Promise<[PermissionStatus, boolean]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
AVCaptureDevice.requestAccessForMediaTypeCompletionHandler(typeFromString(type), (granted) => resolve(getStatus(type)));
|
||||
});
|
||||
}
|
||||
}
|
||||
namespace NSPSpeechRecognition {
|
||||
let status: PermissionStatus = PermissionStatus.undetermined;
|
||||
export function getStatus(): [PermissionStatus, boolean] {
|
||||
const speechStatus = SFSpeechRecognizer.authorizationStatus();
|
||||
switch (speechStatus) {
|
||||
case SFSpeechRecognizerAuthorizationStatus.Authorized:
|
||||
status = PermissionStatus.authorized;
|
||||
break;
|
||||
case SFSpeechRecognizerAuthorizationStatus.Denied:
|
||||
status = PermissionStatus.denied;
|
||||
break;
|
||||
case SFSpeechRecognizerAuthorizationStatus.Restricted:
|
||||
status = PermissionStatus.restricted;
|
||||
break;
|
||||
default:
|
||||
status = PermissionStatus.undetermined;
|
||||
}
|
||||
return [status, true];
|
||||
}
|
||||
|
||||
export function request(): Promise<[PermissionStatus, boolean]> {
|
||||
return new Promise((resolve) => {
|
||||
SFSpeechRecognizer.requestAuthorization(() => resolve(getStatus()));
|
||||
});
|
||||
}
|
||||
}
|
||||
namespace NSPPhoto {
|
||||
let status: PermissionStatus = PermissionStatus.undetermined;
|
||||
export function getStatus(): [PermissionStatus, boolean] {
|
||||
let photoStatus: PHAuthorizationStatus;
|
||||
if (parseFloat(Device.osVersion) >= 14) {
|
||||
photoStatus = PHPhotoLibrary.authorizationStatusForAccessLevel(PHAccessLevel.ReadWrite);
|
||||
} else {
|
||||
photoStatus = PHPhotoLibrary.authorizationStatus();
|
||||
}
|
||||
switch (photoStatus) {
|
||||
case PHAuthorizationStatus.Authorized:
|
||||
status = PermissionStatus.authorized;
|
||||
break;
|
||||
case PHAuthorizationStatus.Denied:
|
||||
status = PermissionStatus.denied;
|
||||
break;
|
||||
case PHAuthorizationStatus.Restricted:
|
||||
status = PermissionStatus.restricted;
|
||||
break;
|
||||
default:
|
||||
status = PermissionStatus.undetermined;
|
||||
}
|
||||
return [status, true];
|
||||
}
|
||||
|
||||
export function request(): Promise<[PermissionStatus, boolean]> {
|
||||
return new Promise((resolve) => {
|
||||
PHPhotoLibrary.requestAuthorization(() => resolve(getStatus()));
|
||||
});
|
||||
}
|
||||
}
|
||||
namespace NSPMotion {
|
||||
let status: PermissionStatus = PermissionStatus.undetermined;
|
||||
export function getStatus(): [PermissionStatus, boolean] {
|
||||
if (status === PermissionStatus.undetermined) {
|
||||
const cmStatus = CMMotionActivityManager.authorizationStatus as any as CMAuthorizationStatus;
|
||||
switch (cmStatus) {
|
||||
case CMAuthorizationStatus.Authorized:
|
||||
status = PermissionStatus.authorized;
|
||||
break;
|
||||
case CMAuthorizationStatus.Denied:
|
||||
status = PermissionStatus.denied;
|
||||
break;
|
||||
case CMAuthorizationStatus.Restricted:
|
||||
status = PermissionStatus.restricted;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [status, true];
|
||||
}
|
||||
|
||||
export function request(): Promise<[PermissionStatus, boolean]> {
|
||||
if (status === PermissionStatus.undetermined) {
|
||||
return new Promise((resolve) => {
|
||||
let activityManager = CMMotionActivityManager.new();
|
||||
let motionActivityQueue = NSOperationQueue.new();
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`NSPMotion request ${status}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
activityManager.queryActivityStartingFromDateToDateToQueueWithHandler(NSDate.distantPast, new Date(), motionActivityQueue, (activities, error) => {
|
||||
if (error) {
|
||||
status = PermissionStatus.denied;
|
||||
} else if (activities || !error) {
|
||||
status = PermissionStatus.authorized;
|
||||
}
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`NSPMotion got response ${status}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
resolve([status, true]);
|
||||
activityManager = null;
|
||||
motionActivityQueue = null;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve([status, true]);
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace NSPMediaLibrary {
|
||||
let status: PermissionStatus = PermissionStatus.undetermined;
|
||||
export function getStatus(): [PermissionStatus, boolean] {
|
||||
const mediaStatus = MPMediaLibrary.authorizationStatus();
|
||||
switch (mediaStatus) {
|
||||
case MPMediaLibraryAuthorizationStatus.Authorized:
|
||||
status = PermissionStatus.authorized;
|
||||
break;
|
||||
case MPMediaLibraryAuthorizationStatus.Denied:
|
||||
status = PermissionStatus.denied;
|
||||
break;
|
||||
case MPMediaLibraryAuthorizationStatus.Restricted:
|
||||
status = PermissionStatus.restricted;
|
||||
break;
|
||||
default:
|
||||
status = PermissionStatus.undetermined;
|
||||
}
|
||||
return [status, true];
|
||||
}
|
||||
|
||||
export function request(): Promise<[PermissionStatus, boolean]> {
|
||||
return new Promise((resolve) => {
|
||||
MPMediaLibrary.requestAuthorization(() => resolve(getStatus()));
|
||||
});
|
||||
}
|
||||
}
|
||||
namespace NSPNotification {
|
||||
let status: PermissionStatus = PermissionStatus.undetermined;
|
||||
const NSPDidAskForNotification = 'NSPDidAskForNotification';
|
||||
export async function getStatus(): Promise<[PermissionStatus, boolean]> {
|
||||
const didAskForPermission = NSUserDefaults.standardUserDefaults.boolForKey(NSPDidAskForNotification);
|
||||
let isEnabled = false;
|
||||
const osVersion = parseFloat(Device.osVersion);
|
||||
if (osVersion >= 10) {
|
||||
isEnabled = (await new Promise<UNNotificationSettings>((resolve) => UNUserNotificationCenter.currentNotificationCenter().getNotificationSettingsWithCompletionHandler(resolve))) !== (UNAuthorizationOptionNone as any);
|
||||
} else {
|
||||
isEnabled = UIApplication.sharedApplication.currentUserNotificationSettings.types !== UIUserNotificationType.None;
|
||||
}
|
||||
|
||||
if (isEnabled) {
|
||||
status = PermissionStatus.authorized;
|
||||
} else {
|
||||
status = didAskForPermission ? PermissionStatus.denied : PermissionStatus.undetermined;
|
||||
}
|
||||
return [status, true];
|
||||
}
|
||||
|
||||
export function request(types: UIUserNotificationType | UNAuthorizationOptions): Promise<[PermissionStatus, boolean]> {
|
||||
const status = getStatus();
|
||||
|
||||
if (status[0] === PermissionStatus.undetermined) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const observer = function () {
|
||||
resolve(getStatus());
|
||||
NSNotificationCenter.defaultCenter.removeObserver(observer);
|
||||
};
|
||||
NSNotificationCenter.defaultCenter.addObserverForNameObjectQueueUsingBlock(UIApplicationDidBecomeActiveNotification, null, null, observer);
|
||||
const osVersion = parseFloat(Device.osVersion);
|
||||
if (osVersion >= 10) {
|
||||
UNUserNotificationCenter.currentNotificationCenter().requestAuthorizationWithOptionsCompletionHandler(types as UNAuthorizationOptions, (p1: boolean, error: NSError) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
UIApplication.sharedApplication.registerForRemoteNotifications();
|
||||
NSUserDefaults.standardUserDefaults.setBoolForKey(true, NSPDidAskForNotification);
|
||||
NSUserDefaults.standardUserDefaults.synchronize();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const settings = UIUserNotificationSettings.settingsForTypesCategories(types as UIUserNotificationType, null);
|
||||
UIApplication.sharedApplication.registerUserNotificationSettings(settings);
|
||||
UIApplication.sharedApplication.registerForRemoteNotifications();
|
||||
|
||||
NSUserDefaults.standardUserDefaults.setBoolForKey(true, NSPDidAskForNotification);
|
||||
NSUserDefaults.standardUserDefaults.synchronize();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace NSPContacts {
|
||||
let status: PermissionStatus = PermissionStatus.undetermined;
|
||||
export function getStatus(): [PermissionStatus, boolean] {
|
||||
const contactStatus = CNContactStore.authorizationStatusForEntityType(CNEntityType.Contacts);
|
||||
switch (contactStatus) {
|
||||
case CNAuthorizationStatus.Authorized:
|
||||
status = PermissionStatus.authorized;
|
||||
break;
|
||||
case CNAuthorizationStatus.Denied:
|
||||
status = PermissionStatus.denied;
|
||||
break;
|
||||
case CNAuthorizationStatus.Restricted:
|
||||
status = PermissionStatus.restricted;
|
||||
break;
|
||||
default:
|
||||
status = PermissionStatus.undetermined;
|
||||
}
|
||||
return [status, true];
|
||||
}
|
||||
|
||||
export function request(): Promise<[PermissionStatus, boolean]> {
|
||||
return new Promise((resolve) => {
|
||||
const contactStore = CNContactStore.new();
|
||||
contactStore.requestAccessForEntityTypeCompletionHandler(CNEntityType.Contacts, () => resolve(getStatus()));
|
||||
});
|
||||
}
|
||||
}
|
||||
namespace NSPBackgroundRefresh {
|
||||
let status: PermissionStatus = PermissionStatus.undetermined;
|
||||
export function getStatus(): [PermissionStatus, boolean] {
|
||||
const refreshStatus = UIApplication.sharedApplication.backgroundRefreshStatus;
|
||||
switch (refreshStatus) {
|
||||
case UIBackgroundRefreshStatus.Available:
|
||||
status = PermissionStatus.authorized;
|
||||
break;
|
||||
case UIBackgroundRefreshStatus.Denied:
|
||||
status = PermissionStatus.denied;
|
||||
break;
|
||||
case UIBackgroundRefreshStatus.Restricted:
|
||||
status = PermissionStatus.restricted;
|
||||
break;
|
||||
default:
|
||||
status = PermissionStatus.undetermined;
|
||||
}
|
||||
return [status, true];
|
||||
}
|
||||
|
||||
export function request(): Promise<PermissionStatus> {
|
||||
return new Promise((resolve) => {
|
||||
const contactStore = CNContactStore.new();
|
||||
contactStore.requestAccessForEntityTypeCompletionHandler(CNEntityType.Contacts, () => resolve(getStatus()[0]));
|
||||
});
|
||||
}
|
||||
}
|
||||
namespace NSPEvent {
|
||||
let status: PermissionStatus = PermissionStatus.undetermined;
|
||||
function typeFromString(value: string) {
|
||||
if (value === 'reminder') {
|
||||
return EKEntityType.Reminder;
|
||||
} else {
|
||||
return EKEntityType.Event;
|
||||
}
|
||||
}
|
||||
export function getStatus(type?: string): [PermissionStatus, boolean] {
|
||||
const eventStatus = EKEventStore.authorizationStatusForEntityType(typeFromString(type));
|
||||
switch (eventStatus) {
|
||||
case EKAuthorizationStatus.Authorized:
|
||||
status = PermissionStatus.authorized;
|
||||
break;
|
||||
case EKAuthorizationStatus.Denied:
|
||||
status = PermissionStatus.denied;
|
||||
break;
|
||||
case EKAuthorizationStatus.Restricted:
|
||||
status = PermissionStatus.restricted;
|
||||
break;
|
||||
default:
|
||||
status = PermissionStatus.undetermined;
|
||||
}
|
||||
return [status, true];
|
||||
}
|
||||
|
||||
export function request(type?: string): Promise<[PermissionStatus, boolean]> {
|
||||
return new Promise((resolve) => {
|
||||
const aStore = EKEventStore.new();
|
||||
aStore.requestAccessToEntityTypeCompletion(typeFromString(type), () => resolve(getStatus(type)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export enum NSType {
|
||||
Location = 'location',
|
||||
Camera = 'camera',
|
||||
Microphone = 'microphone',
|
||||
Photo = 'photo',
|
||||
Contacts = 'contacts',
|
||||
Event = 'event',
|
||||
Reminder = 'reminder',
|
||||
Bluetooth = 'bluetooth',
|
||||
Notification = 'notification',
|
||||
BackgroundRefresh = 'backgroundRefresh',
|
||||
NSPTypeSpeechRecognition = 'speechRecognition',
|
||||
MediaLibrary = 'mediaLibrary',
|
||||
Motion = 'motion',
|
||||
}
|
||||
|
||||
export function openSettings() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const center = NSNotificationCenter.defaultCenter;
|
||||
const observer = function (notif) {
|
||||
resolve(true);
|
||||
center.removeObserver(observer);
|
||||
};
|
||||
center.addObserverForNameObjectQueueUsingBlock(UIApplicationDidBecomeActiveNotification, null, null, observer);
|
||||
UIApplication.sharedApplication.openURL(NSURL.URLWithString(UIApplicationOpenSettingsURLString));
|
||||
});
|
||||
}
|
||||
export function canOpenSettings() {
|
||||
return Promise.resolve(UIApplicationOpenSettingsURLString !== null);
|
||||
}
|
||||
export async function getPermissionStatus(type, json): Promise<[PermissionStatus, boolean]> {
|
||||
let status: [PermissionStatus, boolean];
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`getPermissionStatus ${type}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case NSType.Location: {
|
||||
// NSString *locationPermissionType = [RCTConvert NSString:json];
|
||||
status = NSPLocation.getStatusForType(json);
|
||||
break;
|
||||
}
|
||||
case NSType.Camera:
|
||||
status = NSPAudioVideo.getStatus('video');
|
||||
break;
|
||||
case NSType.Microphone:
|
||||
status = NSPAudioVideo.getStatus('audio');
|
||||
break;
|
||||
case NSType.Photo:
|
||||
status = NSPPhoto.getStatus();
|
||||
break;
|
||||
case NSType.Contacts:
|
||||
status = NSPContacts.getStatus();
|
||||
break;
|
||||
case NSType.Event:
|
||||
status = NSPEvent.getStatus('event');
|
||||
break;
|
||||
case NSType.Reminder:
|
||||
status = NSPEvent.getStatus('reminder');
|
||||
break;
|
||||
case NSType.Bluetooth:
|
||||
status = NSPBluetooth.getStatus();
|
||||
break;
|
||||
case NSType.Notification:
|
||||
status = await NSPNotification.getStatus();
|
||||
break;
|
||||
case NSType.BackgroundRefresh:
|
||||
status = NSPBackgroundRefresh.getStatus();
|
||||
break;
|
||||
case NSType.NSPTypeSpeechRecognition:
|
||||
status = NSPSpeechRecognition.getStatus();
|
||||
break;
|
||||
case NSType.MediaLibrary:
|
||||
status = NSPMediaLibrary.getStatus();
|
||||
break;
|
||||
case NSType.Motion:
|
||||
status = NSPMotion.getStatus();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
export function requestPermission(type, json): Promise<[PermissionStatus, boolean]> {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`requestPermission ${type}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
switch (type) {
|
||||
case NSType.Location:
|
||||
return NSPLocation.request(json);
|
||||
case NSType.Camera:
|
||||
return NSPAudioVideo.request('video');
|
||||
case NSType.Microphone:
|
||||
return NSPAudioVideo.request('audio');
|
||||
case NSType.Photo:
|
||||
return NSPPhoto.request();
|
||||
case NSType.Contacts:
|
||||
return NSPContacts.request();
|
||||
case NSType.Event:
|
||||
return NSPEvent.request('event');
|
||||
case NSType.Reminder:
|
||||
return NSPEvent.request('reminder');
|
||||
case NSType.Bluetooth:
|
||||
return NSPBluetooth.request();
|
||||
case NSType.Notification:
|
||||
let types: UIUserNotificationType;
|
||||
const typeStrings: string[] = json;
|
||||
const osVersion = parseFloat(Device.osVersion);
|
||||
if (osVersion >= 10) {
|
||||
if (typeStrings.indexOf('alert') !== -1) {
|
||||
types = types | UNAuthorizationOptions.Alert;
|
||||
}
|
||||
if (typeStrings.indexOf('badge') !== -1) {
|
||||
types = types | UNAuthorizationOptions.Badge;
|
||||
}
|
||||
if (typeStrings.indexOf('sound') !== -1) {
|
||||
types = types | UNAuthorizationOptions.Sound;
|
||||
}
|
||||
if (typeStrings.indexOf('providesAppNotificationSettings') !== -1 && parseFloat(Device.osVersion) >= 12) {
|
||||
types = types | UNAuthorizationOptions.ProvidesAppNotificationSettings;
|
||||
}
|
||||
} else {
|
||||
if (typeStrings.indexOf('alert') !== -1) {
|
||||
types = types | UIUserNotificationType.Alert;
|
||||
}
|
||||
if (typeStrings.indexOf('badge') !== -1) {
|
||||
types = types | UIUserNotificationType.Badge;
|
||||
}
|
||||
if (typeStrings.indexOf('sound') !== -1) {
|
||||
types = types | UIUserNotificationType.Sound;
|
||||
}
|
||||
}
|
||||
|
||||
return NSPNotification.request(types);
|
||||
case NSType.NSPTypeSpeechRecognition:
|
||||
return NSPSpeechRecognition.request();
|
||||
case NSType.MediaLibrary:
|
||||
return NSPMediaLibrary.request();
|
||||
case NSType.Motion:
|
||||
return NSPMotion.request();
|
||||
default:
|
||||
return Promise.reject('unknown');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULTS = {
|
||||
location: 'whenInUse',
|
||||
notification: ['alert', 'badge', 'sound'],
|
||||
};
|
||||
|
||||
type IOSPermissionTypes = `${PermissionsIOS.NSType}`;
|
||||
const permissionTypes = Object.values(PermissionsIOS.NSType) as IOSPermissionTypes[];
|
||||
type SingleResult = [PermissionStatus, boolean];
|
||||
interface MultipleResult {
|
||||
[k: string]: PermissionStatus;
|
||||
}
|
||||
type Result<T> = T extends any[] ? MultipleResult : SingleResult;
|
||||
|
||||
export class Permissions {
|
||||
static canOpenSettings() {
|
||||
return PermissionsIOS.canOpenSettings();
|
||||
}
|
||||
static openSettings() {
|
||||
return PermissionsIOS.openSettings();
|
||||
}
|
||||
static getTypes() {
|
||||
return permissionTypes;
|
||||
}
|
||||
static check(permission: IOSPermissionTypes, options?: PermissionCheckOptions): Promise<SingleResult> {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`check ${permission}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
if (permissionTypes.indexOf(permission) === -1) {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`'${permission}' is not a valid permission type on iOS`, Trace.categories.Permissions, Trace.messageType.warn);
|
||||
}
|
||||
|
||||
return Promise.resolve([PermissionStatus.authorized, true]);
|
||||
}
|
||||
|
||||
let type: PermissionCheckOptions;
|
||||
|
||||
if (typeof options === 'string') {
|
||||
type = options;
|
||||
} else if (options && options.type) {
|
||||
type = options.type;
|
||||
}
|
||||
|
||||
return PermissionsIOS.getPermissionStatus(permission, type || DEFAULTS[permission]);
|
||||
}
|
||||
static async request<T extends IOSPermissionTypes | IOSPermissionTypes[]>(permission: T, options?: PermissionRequestOptions): Promise<Result<T>> {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`request ${permission}`, Trace.categories.Permissions, Trace.messageType.info);
|
||||
}
|
||||
if (Array.isArray(permission)) {
|
||||
const grantedPermissions: Result<IOSPermissionTypes[]> = {};
|
||||
for (let index = 0; index < permission.length; index++) {
|
||||
const res = await Permissions.request(permission[index], options);
|
||||
grantedPermissions[permission[index]] = res[0];
|
||||
}
|
||||
return Promise.resolve(grantedPermissions as Result<T>);
|
||||
}
|
||||
if (permissionTypes.indexOf(permission) === -1) {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`'${permission}' is not a valid permission type on iOS`, Trace.categories.Permissions, Trace.messageType.warn);
|
||||
}
|
||||
|
||||
return Promise.resolve([PermissionStatus.authorized, true] as Result<T>);
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
if (permission === 'backgroundRefresh') {
|
||||
throw new Error('You cannot request backgroundRefresh');
|
||||
}
|
||||
|
||||
let type: PermissionRequestOptions;
|
||||
|
||||
if (typeof options === 'string') {
|
||||
type = options;
|
||||
} else if (options && options.type) {
|
||||
type = options.type;
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
return PermissionsIOS.requestPermission(permission, type || DEFAULTS[permission]);
|
||||
}
|
||||
static checkMultiple(permissions: PermissionsType[]) {
|
||||
return Promise.all(permissions.map((permission) => Permissions.check(<any>permission))).then((result) =>
|
||||
result.reduce((acc, value, index) => {
|
||||
const name = permissions[index];
|
||||
acc[name] = value;
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user