Skip to content

Commit

Permalink
Merge branch '2.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
zoontek committed Apr 11, 2020
2 parents 6145671 + 23a6b22 commit cc51259
Show file tree
Hide file tree
Showing 37 changed files with 2,598 additions and 2,399 deletions.
108 changes: 53 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,31 +103,17 @@ Then update your `Info.plist` with wanted permissions usage descriptions:
</plist>
```

#### ⚠️ If you encounter the error `Invalid RNPermission X. Should be one of: ()`
## Workaround for `use_frameworks!` issues

1. Check that you linked **at least one** permission handler.
2. Clean up Xcode stale data with `npx react-native-clean-project --remove-iOS-build --remove-iOS-pods`
3. If you use `use_frameworks!`, replace it by `use_modular_headers!` - see [this blog post](http://blog.cocoapods.org/CocoaPods-1.5.0) for more details. [Create empty Swift file in XCode](https://stackoverflow.com/questions/52536380/why-linker-link-static-libraries-with-errors-ios/56176956#56176956). Then add ":modular_headers => false" to Pods with build errors:
If you use `use_frameworks!`, add this at the top of your `Podfile`:

```ruby
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec', :modular_headers => false
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec', :modular_headers => false
```

4. If you use `use_frameworks!` but **can't** replace it with `use_modular_headers!`, check the following workaround:

```ruby
# Add this code at the top of Podfile right after platform definition.
# It will make all the dynamic frameworks turning into static libraries.

use_frameworks!

$dynamic_frameworks = ['RxCocoa', 'RxSwift', 'WhatEverSDKName']

# Convert all permissions handlers into static libraries
pre_install do |installer|
installer.pod_targets.each do |pod|
if !$dynamic_frameworks.include?(pod.name)
puts "Link #{pod.name} as static_library"
if pod.name.start_with?('Permission-')
def pod.build_type;
# Uncomment one line depending on your CocoaPods version
# Pod::BuildType.static_library # >= 1.9
Expand Down Expand Up @@ -435,7 +421,7 @@ function check(permission: string): Promise<PermissionStatus>;
import {check, PERMISSIONS, RESULTS} from 'react-native-permissions';

check(PERMISSIONS.IOS.LOCATION_ALWAYS)
.then(result => {
.then((result) => {
switch (result) {
case RESULTS.UNAVAILABLE:
console.log(
Expand All @@ -455,7 +441,7 @@ check(PERMISSIONS.IOS.LOCATION_ALWAYS)
break;
}
})
.catch(error => {
.catch((error) => {
//
});
```
Expand Down Expand Up @@ -484,7 +470,7 @@ function request(
```js
import {request, PERMISSIONS} from 'react-native-permissions';

request(PERMISSIONS.IOS.LOCATION_ALWAYS).then(result => {
request(PERMISSIONS.IOS.LOCATION_ALWAYS).then((result) => {
//
});
```
Expand Down Expand Up @@ -568,6 +554,52 @@ requestNotifications(['alert', 'sound']).then(({status, settings}) => {

---

#### checkMultiple

Check multiples permissions in parallel.

```ts
function checkMultiple<P extends Permission[]>(
permissions: P,
): Promise<Record<P[number], PermissionStatus>>;
```

```js
import {checkMultiple, PERMISSIONS} from 'react-native-permissions';

checkMultiple([PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.FACE_ID]).then(
(statuses) => {
console.log('Camera', statuses[PERMISSIONS.IOS.CAMERA]);
console.log('FaceID', statuses[PERMISSIONS.IOS.FACE_ID]);
},
);
```

---

#### requestMultiple

Request multiple permissions in sequence.

```ts
function requestMultiple<P extends Permission[]>(
permissions: P,
): Promise<Record<P[number], PermissionStatus>>;
```

```js
import {requestMultiple, PERMISSIONS} from 'react-native-permissions';

requestMultiple([PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.FACE_ID]).then(
(statuses) => {
console.log('Camera', statuses[PERMISSIONS.IOS.CAMERA]);
console.log('FaceID', statuses[PERMISSIONS.IOS.FACE_ID]);
},
);
```

---

#### openSettings

Open application settings.
Expand Down Expand Up @@ -603,37 +635,3 @@ request(
}),
);
```

## Additional recipes

#### Check multiples permissions

```js
import {check, PERMISSIONS} from 'react-native-permissions';

// can be done in parallel
Promise.all([
check(PERMISSIONS.IOS.CAMERA),
check(PERMISSIONS.IOS.CONTACTS),
//
]).then(([cameraStatus, contactsStatus /**/]) => {
console.log({cameraStatus, contactsStatus});
});
```

#### Request multiples permissions

_⚠️  It's a very bad UX pattern, avoid doing it!_

```js
import {request, PERMISSIONS} from 'react-native-permissions';

// should be done in sequence
async function requestAll() {
const cameraStatus = await request(PERMISSIONS.IOS.CAMERA);
const contactsStatus = await request(PERMISSIONS.IOS.CONTACTS);
return {cameraStatus, contactsStatus};
}

requestAll().then(statuses => console.log(statuses));
```
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,50 @@
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Nullable;

@ReactModule(name = RNPermissionsModule.MODULE_NAME)
public class RNPermissionsModule extends ReactContextBaseJavaModule {

private static final String ERROR_INVALID_ACTIVITY = "E_INVALID_ACTIVITY";
public static final String MODULE_NAME = "RNPermissions";
private static final String SETTING_NAME = "@RNPermissions:NonRequestables";

private static final String[][] PERMISSIONS = new String[][] {
{ "ACCEPT_HANDOVER", "android.permission.ACCEPT_HANDOVER" },
{ "ACCESS_BACKGROUND_LOCATION", "android.permission.ACCESS_BACKGROUND_LOCATION" },
{ "ACCESS_COARSE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION" },
{ "ACCESS_FINE_LOCATION", "android.permission.ACCESS_FINE_LOCATION" },
{ "ACTIVITY_RECOGNITION", "android.permission.ACTIVITY_RECOGNITION" },
{ "ADD_VOICEMAIL", "com.android.voicemail.permission.ADD_VOICEMAIL" },
{ "ANSWER_PHONE_CALLS", "android.permission.ANSWER_PHONE_CALLS" },
{ "BODY_SENSORS", "android.permission.BODY_SENSORS" },
{ "CALL_PHONE", "android.permission.CALL_PHONE" },
{ "CAMERA", "android.permission.CAMERA" },
{ "GET_ACCOUNTS", "android.permission.GET_ACCOUNTS" },
{ "PROCESS_OUTGOING_CALLS", "android.permission.PROCESS_OUTGOING_CALLS" },
{ "READ_CALENDAR", "android.permission.READ_CALENDAR" },
{ "READ_CALL_LOG", "android.permission.READ_CALL_LOG" },
{ "READ_CONTACTS", "android.permission.READ_CONTACTS" },
{ "READ_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE" },
{ "READ_PHONE_NUMBERS", "android.permission.READ_PHONE_NUMBERS" },
{ "READ_PHONE_STATE", "android.permission.READ_PHONE_STATE" },
{ "READ_SMS", "android.permission.READ_SMS" },
{ "RECEIVE_MMS", "android.permission.RECEIVE_MMS" },
{ "RECEIVE_SMS", "android.permission.RECEIVE_SMS" },
{ "RECEIVE_WAP_PUSH", "android.permission.RECEIVE_WAP_PUSH" },
{ "RECORD_AUDIO", "android.permission.RECORD_AUDIO" },
{ "SEND_SMS", "android.permission.SEND_SMS" },
{ "USE_SIP", "android.permission.USE_SIP" },
{ "WRITE_CALENDAR", "android.permission.WRITE_CALENDAR" },
{ "WRITE_CALL_LOG", "android.permission.WRITE_CALL_LOG" },
{ "WRITE_CONTACTS", "android.permission.WRITE_CONTACTS" },
{ "WRITE_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE" },
};

private final SharedPreferences sharedPrefs;

public RNPermissionsModule(ReactApplicationContext reactContext) {
Expand All @@ -40,75 +75,92 @@ public String getName() {
return MODULE_NAME;
}

@ReactMethod
public void isAvailable(final String permission, final Promise promise) {
String fieldName = permission.substring(permission.lastIndexOf('.') + 1);

private boolean fieldExists(final String fieldName) {
try {
Manifest.permission.class.getField(fieldName);
promise.resolve(true);
} catch (NoSuchFieldException e) {
promise.resolve(false);
return true;
} catch (NoSuchFieldException ignored) {
return false;
}
}

@Override
public @Nullable Map<String, Object> getConstants() {
HashMap<String, Object> constants = new HashMap<>();
WritableArray available = Arguments.createArray();

for (String[] permission : PERMISSIONS) {
if (fieldExists(permission[0]))
available.pushString(permission[1]);
}

constants.put("available", available);
return constants;
}

@ReactMethod
public void setNonRequestable(final String permission, final Promise promise) {
promise.resolve(sharedPrefs.edit().putBoolean(permission, true).commit());
public void isNonRequestable(final String permission, final Promise promise) {
promise.resolve(sharedPrefs.getBoolean(permission, false));
}

@ReactMethod
public void getNonRequestables(final Promise promise) {
WritableArray result = Arguments.createArray();
WritableArray output = Arguments.createArray();
Map<String, ?> entries = sharedPrefs.getAll();

for (Map.Entry<String, ?> entry : entries.entrySet()) {
result.pushString(entry.getKey());
}
for (Map.Entry<String, ?> entry : entries.entrySet())
output.pushString(entry.getKey());

promise.resolve(result);
promise.resolve(output);
}

@ReactMethod
public void openSettings(final Promise promise) {
try {
final ReactApplicationContext reactContext = getReactApplicationContext();
final Intent intent = new Intent();
public void setNonRequestable(final String permission, final Promise promise) {
promise.resolve(sharedPrefs.edit().putBoolean(permission, true).commit());
}

intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.fromParts("package", reactContext.getPackageName(), null));
@ReactMethod
public void setNonRequestables(final ReadableArray permissions, final Promise promise) {
SharedPreferences.Editor editor = sharedPrefs.edit();

reactContext.startActivity(intent);
promise.resolve(true);
} catch (Exception e) {
promise.reject(ERROR_INVALID_ACTIVITY, e);
}
for (int i = 0; i < permissions.size(); i++)
editor.putBoolean(permissions.getString(i), true);

promise.resolve(editor.commit());
}

private WritableMap internalCheckNotifications() {
final ReactApplicationContext reactContext = getReactApplicationContext();
final boolean enabled = NotificationManagerCompat.from(reactContext).areNotificationsEnabled();
final WritableMap map = Arguments.createMap();
@ReactMethod
public void checkNotifications(final Promise promise) {
final boolean enabled = NotificationManagerCompat
.from(getReactApplicationContext()).areNotificationsEnabled();
final WritableMap output = Arguments.createMap();
final WritableMap settings = Arguments.createMap();

if (enabled) {
map.putString("status", "granted");
output.putString("status", "granted");
} else {
map.putString("status", "blocked");
output.putString("status", "blocked");
}

map.putMap("settings", settings);
return map;
output.putMap("settings", settings);
promise.resolve(output);
}

@ReactMethod
public void checkNotifications(final Promise promise) {
promise.resolve(internalCheckNotifications());
}
public void openSettings(final Promise promise) {
try {
final ReactApplicationContext reactContext = getReactApplicationContext();
final Intent intent = new Intent();
final String packageName = reactContext.getPackageName();

@ReactMethod
public void requestNotifications(ReadableArray options, final Promise promise) {
promise.resolve(internalCheckNotifications());
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.fromParts("package", packageName, null));

reactContext.startActivity(intent);
promise.resolve(true);
} catch (Exception e) {
promise.reject(ERROR_INVALID_ACTIVITY, e);
}
}
}
Loading

0 comments on commit cc51259

Please sign in to comment.