1 | import { Platform } from 'react-native';
|
2 |
|
3 | import {
|
4 | coalesceExpirations,
|
5 | coalesceStatuses,
|
6 | coalesceCanAskAgain,
|
7 | coalesceGranted,
|
8 | } from './CoalescedPermissions';
|
9 | import Permissions from './ExpoPermissions';
|
10 | import {
|
11 | PermissionResponse,
|
12 | PermissionType,
|
13 | PermissionMap,
|
14 | PermissionStatus,
|
15 | PermissionExpiration,
|
16 | PermissionInfo,
|
17 | } from './Permissions.types';
|
18 |
|
19 | export {
|
20 | PermissionStatus,
|
21 | PermissionResponse,
|
22 | PermissionExpiration,
|
23 | PermissionMap,
|
24 | PermissionInfo,
|
25 | PermissionType,
|
26 | };
|
27 |
|
28 | export const CAMERA = 'camera';
|
29 | export const MEDIA_LIBRARY = 'mediaLibrary';
|
30 | export const MEDIA_LIBRARY_WRITE_ONLY = 'mediaLibraryWriteOnly';
|
31 |
|
32 |
|
33 |
|
34 | export const CAMERA_ROLL = MEDIA_LIBRARY;
|
35 | export const AUDIO_RECORDING = 'audioRecording';
|
36 |
|
37 | export const LOCATION = 'location';
|
38 | export const LOCATION_FOREGROUND = 'locationForeground';
|
39 | export const LOCATION_BACKGROUND = 'locationBackground';
|
40 | export const USER_FACING_NOTIFICATIONS = 'userFacingNotifications';
|
41 | export const NOTIFICATIONS = 'notifications';
|
42 | export const CONTACTS = 'contacts';
|
43 | export const CALENDAR = 'calendar';
|
44 | export const REMINDERS = 'reminders';
|
45 | export const SYSTEM_BRIGHTNESS = 'systemBrightness';
|
46 | export const MOTION = 'motion';
|
47 |
|
48 |
|
49 | const PERMISSION_MODULE_MAPPING = {
|
50 | [CAMERA]: 'expo-camera',
|
51 | [CAMERA_ROLL]: 'expo-media-library',
|
52 | [MEDIA_LIBRARY]: 'expo-media-library',
|
53 | [MEDIA_LIBRARY_WRITE_ONLY]: 'expo-media-library',
|
54 | [AUDIO_RECORDING]: 'expo-av',
|
55 | [LOCATION]: 'expo-location',
|
56 | [USER_FACING_NOTIFICATIONS]: 'expo-notifications',
|
57 | [NOTIFICATIONS]: 'expo-notifications',
|
58 | [CONTACTS]: 'expo-contacts',
|
59 | [CALENDAR]: 'expo-calendar',
|
60 | [REMINDERS]: 'expo-calendar',
|
61 | [SYSTEM_BRIGHTNESS]: 'expo-brightness',
|
62 | [MOTION]: 'expo-sensors',
|
63 | };
|
64 |
|
65 | export async function getAsync(...types: PermissionType[]): Promise<PermissionResponse> {
|
66 | console.warn(
|
67 | `expo-permissions is now deprecated — the functionality has been moved to other expo packages that directly use these permissions (e.g. expo-location, expo-camera). The package will be removed in the upcoming releases.`
|
68 | );
|
69 |
|
70 | if (Platform.OS === 'ios') {
|
71 | return await _handleMultiPermissionsRequestIOSAsync(types, Permissions.getAsync);
|
72 | }
|
73 | return await _handlePermissionsRequestAsync(types, Permissions.getAsync);
|
74 | }
|
75 |
|
76 | export async function askAsync(...types: PermissionType[]): Promise<PermissionResponse> {
|
77 | console.warn(
|
78 | `expo-permissions is now deprecated — the functionality has been moved to other expo packages that directly use these permissions (e.g. expo-location, expo-camera). The package will be removed in the upcoming releases.`
|
79 | );
|
80 |
|
81 | if (Platform.OS === 'ios') {
|
82 | return await _handleMultiPermissionsRequestIOSAsync(types, Permissions.askAsync);
|
83 | }
|
84 | return await _handlePermissionsRequestAsync(types, Permissions.askAsync);
|
85 | }
|
86 |
|
87 | async function _handleSinglePermissionRequestIOSAsync(
|
88 | type: PermissionType,
|
89 | handlePermission: (type: PermissionType) => Promise<PermissionInfo>
|
90 | ): Promise<PermissionInfo> {
|
91 | if (Platform.OS !== 'web' && type === 'motion') {
|
92 | return {
|
93 | status: PermissionStatus.GRANTED,
|
94 | expires: 'never',
|
95 | granted: true,
|
96 | canAskAgain: true,
|
97 | };
|
98 | }
|
99 | try {
|
100 | return await handlePermission(type);
|
101 | } catch (error) {
|
102 |
|
103 | if (error.code === 'E_PERMISSIONS_UNKNOWN' && PERMISSION_MODULE_MAPPING[type]) {
|
104 | const library = PERMISSION_MODULE_MAPPING[type];
|
105 | error.message = `${error.message}, please install and link the package ${PERMISSION_MODULE_MAPPING[type]}, see more at https://github.com/expo/expo/tree/master/packages/${library}`;
|
106 | }
|
107 | throw error;
|
108 | }
|
109 | }
|
110 |
|
111 | async function _handleMultiPermissionsRequestIOSAsync(
|
112 | types: PermissionType[],
|
113 | handlePermission: (type: PermissionType) => Promise<PermissionInfo>
|
114 | ): Promise<PermissionResponse> {
|
115 | if (!types.length) {
|
116 | throw new Error('At least one permission type must be specified');
|
117 | }
|
118 |
|
119 | const permissions = {};
|
120 | for (const type of types) {
|
121 | permissions[type] = await _handleSinglePermissionRequestIOSAsync(type, handlePermission);
|
122 | }
|
123 |
|
124 | return {
|
125 | status: coalesceStatuses(permissions),
|
126 | expires: coalesceExpirations(permissions),
|
127 | canAskAgain: coalesceCanAskAgain(permissions),
|
128 | granted: coalesceGranted(permissions),
|
129 | permissions,
|
130 | };
|
131 | }
|
132 |
|
133 | async function _handlePermissionsRequestAsync(
|
134 | types: PermissionType[],
|
135 | handlePermissions: (types: PermissionType[]) => Promise<PermissionMap>
|
136 | ): Promise<PermissionResponse> {
|
137 | if (!types.length) {
|
138 | throw new Error('At least one permission type must be specified');
|
139 | }
|
140 |
|
141 | if (Platform.OS !== 'web' && types.length === 1 && types[0] === 'motion') {
|
142 | const approvedPermission = {
|
143 | status: PermissionStatus.GRANTED,
|
144 | expires: 'never',
|
145 | granted: true,
|
146 | canAskAgain: true,
|
147 | };
|
148 | return {
|
149 | ...approvedPermission,
|
150 |
|
151 | permissions: { motion: approvedPermission },
|
152 | };
|
153 | }
|
154 |
|
155 | const permissions = await handlePermissions(types);
|
156 | return {
|
157 | status: coalesceStatuses(permissions),
|
158 | expires: coalesceExpirations(permissions),
|
159 | canAskAgain: coalesceCanAskAgain(permissions),
|
160 | granted: coalesceGranted(permissions),
|
161 | permissions,
|
162 | };
|
163 | }
|