Skip to content

Commit 8733c69

Browse files
committed
feat: add Android impl
1 parent 6c86baa commit 8733c69

File tree

4 files changed

+309
-10
lines changed

4 files changed

+309
-10
lines changed

android/build.gradle

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,57 @@ buildscript {
1919

2020
apply plugin: "com.android.library"
2121

22+
def resolveReactNativeDirectory() {
23+
// monorepo workaround
24+
// react-native can be hoisted or in project's own node_modules
25+
def reactNativeFromProjectNodeModules = file("${rootProject.projectDir}/../node_modules/react-native")
26+
if (reactNativeFromProjectNodeModules.exists()) {
27+
return reactNativeFromProjectNodeModules
28+
}
29+
30+
def reactNativeFromNodeModulesWithLibrary = file("${projectDir}/../../react-native")
31+
if (reactNativeFromNodeModulesWithLibrary.exists()) {
32+
return reactNativeFromNodeModulesWithLibrary
33+
}
34+
35+
throw new Exception(
36+
"[react-native-clipboard] Unable to resolve react-native location in " +
37+
"node_modules. You should add project extension property (in app/build.gradle) " +
38+
"`REACT_NATIVE_NODE_MODULES_DIR` with path to react-native."
39+
)
40+
}
41+
42+
def REACT_NATIVE_DIR = resolveReactNativeDirectory()
43+
44+
def reactProperties = new Properties()
45+
file("$REACT_NATIVE_DIR/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
46+
47+
def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME")
48+
def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 1000 : REACT_NATIVE_VERSION.split("\\.")[1].toInteger()
49+
50+
def isNewArchitectureEnabled() {
51+
// To opt-in for the New Architecture, you can either:
52+
// - Set `newArchEnabled` to true inside the `gradle.properties` file
53+
// - Invoke gradle with `-newArchEnabled=true`
54+
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
55+
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
56+
}
57+
58+
if (isNewArchitectureEnabled()) {
59+
apply plugin: "com.facebook.react"
60+
}
61+
2262
android {
63+
64+
// Used to override the NDK path/version on internal CI or by allowing
65+
// users to customize the NDK path/version from their root project (e.g. for M1 support)
66+
if (rootProject.hasProperty("ndkPath")) {
67+
ndkPath rootProject.ext.ndkPath
68+
}
69+
if (rootProject.hasProperty("ndkVersion")) {
70+
ndkVersion rootProject.ext.ndkVersion
71+
}
72+
2373
compileSdkVersion safeExtGet("compileSdkVersion", 33)
2474
buildToolsVersion safeExtGet("buildToolsVersion", "33.0.0")
2575
defaultConfig {
@@ -29,6 +79,15 @@ android {
2979
lintOptions {
3080
abortOnError false
3181
}
82+
83+
84+
sourceSets.main {
85+
java {
86+
if (!isNewArchitectureEnabled()) {
87+
srcDirs += 'src/paper/java'
88+
}
89+
}
90+
}
3291
}
3392

3493
repositories {
@@ -46,6 +105,10 @@ repositories {
46105
}
47106

48107
dependencies {
49-
//noinspection GradleDynamicVersion
50-
implementation "com.facebook.react:react-native:+" // From node_modules
108+
if (isNewArchitectureEnabled() && REACT_NATIVE_MINOR_VERSION < 71) {
109+
implementation project(":ReactAndroid")
110+
} else {
111+
//noinspection GradleDynamicVersion
112+
implementation 'com.facebook.react:react-native:+'
113+
}
51114
}

android/src/main/java/com/zoontek/rnpermissions/RNPermissionsModule.java

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@
2828
import com.facebook.react.modules.core.PermissionListener;
2929

3030
import java.util.ArrayList;
31+
import java.util.Map;
3132

32-
@ReactModule(name = RNPermissionsModule.MODULE_NAME)
33-
public class RNPermissionsModule extends ReactContextBaseJavaModule implements PermissionListener {
33+
@ReactModule(name = RNPermissionsModule.NAME)
34+
public class RNPermissionsModule extends NativePermissionsModuleSpec implements PermissionListener {
3435

3536
private static final String ERROR_INVALID_ACTIVITY = "E_INVALID_ACTIVITY";
36-
public static final String MODULE_NAME = "RNPermissions";
37+
public static final String NAME = "RNPermissionsModule";
3738

3839
private final SparseArray<Callback> mCallbacks;
3940
private int mRequestCode = 0;
@@ -49,7 +50,7 @@ public RNPermissionsModule(ReactApplicationContext reactContext) {
4950

5051
@Override
5152
public String getName() {
52-
return MODULE_NAME;
53+
return NAME;
5354
}
5455

5556
private @Nullable String getFieldName(final String permission) {
@@ -373,6 +374,41 @@ public void invoke(Object... args) {
373374
}
374375
}
375376

377+
@Override
378+
protected Map<String, Object> getTypedExportedConstants() {
379+
return null;
380+
}
381+
382+
@Override
383+
public void check(double permission, Promise promise) {
384+
promise.reject("Permissions:check", "check is not supported on Android");
385+
}
386+
387+
@Override
388+
public void checkLocationAccuracy(Promise promise) {
389+
promise.reject("Permissions:checkLocationAccuracy", "checkLocationAccuracy is not supported on Android");
390+
}
391+
392+
@Override
393+
public void request(double permission, Promise promise) {
394+
promise.reject("Permissions:request", "request is not supported on Android");
395+
}
396+
397+
@Override
398+
public void requestLocationAccuracy(String purposeKey, Promise promise) {
399+
promise.reject("Permissions:requestLocationAccuracy", "requestLocationAccuracy is not supported on Android");
400+
}
401+
402+
@Override
403+
public void requestNotifications(ReadableArray options, Promise promise) {
404+
promise.reject("Permissions:requestNotifications", "requestNotifications is not supported on Android");
405+
}
406+
407+
@Override
408+
public void openLimitedPhotoLibraryPicker(Promise promise) {
409+
promise.reject("Permissions:openLimitedPhotoLibraryPicker", "openLimitedPhotoLibraryPicker is not supported on Android");
410+
}
411+
376412
@Override
377413
public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
378414
mCallbacks.get(requestCode).invoke(grantResults, getPermissionAwareActivity());

android/src/main/java/com/zoontek/rnpermissions/RNPermissionsPackage.java

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,102 @@
11
package com.zoontek.rnpermissions;
22

3-
import com.facebook.react.ReactPackage;
3+
import java.util.Collections;
4+
import java.util.HashMap;
5+
import java.util.List;
6+
import java.util.Map;
7+
8+
import com.facebook.react.TurboReactPackage;
9+
import com.facebook.react.ViewManagerOnDemandReactPackage;
10+
import com.facebook.react.bridge.ModuleSpec;
411
import com.facebook.react.bridge.NativeModule;
512
import com.facebook.react.bridge.ReactApplicationContext;
13+
import com.facebook.react.module.annotations.ReactModule;
14+
import com.facebook.react.module.annotations.ReactModuleList;
15+
import com.facebook.react.module.model.ReactModuleInfo;
16+
import com.facebook.react.module.model.ReactModuleInfoProvider;
17+
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
618
import com.facebook.react.uimanager.ViewManager;
719

8-
import java.util.Collections;
9-
import java.util.List;
20+
import javax.annotation.Nonnull;
21+
import javax.annotation.Nullable;
22+
23+
@ReactModuleList(
24+
nativeModules = {
25+
RNPermissionsModule.class,
26+
})
27+
public class RNPermissionsPackage extends TurboReactPackage implements ViewManagerOnDemandReactPackage {
28+
29+
/** {@inheritDoc} */
30+
@Override
31+
public List<String> getViewManagerNames(ReactApplicationContext reactContext) {
32+
return null;
33+
}
34+
35+
@Override
36+
protected List<ModuleSpec> getViewManagers(ReactApplicationContext reactContext) {
37+
return null;
38+
}
1039

11-
public class RNPermissionsPackage implements ReactPackage {
40+
/** {@inheritDoc} */
41+
@Override
42+
public @Nullable
43+
ViewManager createViewManager(
44+
ReactApplicationContext reactContext, String viewManagerName) {
45+
return null;
46+
}
47+
48+
@Override
49+
public NativeModule getModule(String name, @Nonnull ReactApplicationContext reactContext) {
50+
switch (name) {
51+
case RNPermissionsModule.NAME:
52+
return new RNPermissionsModule(reactContext);
53+
default:
54+
return null;
55+
}
56+
}
57+
58+
@Override
59+
public ReactModuleInfoProvider getReactModuleInfoProvider() {
60+
try {
61+
Class<?> reactModuleInfoProviderClass =
62+
Class.forName("com.zoontek.rnpermissions.RNPermissionsPackage$$ReactModuleInfoProvider");
63+
return (ReactModuleInfoProvider) reactModuleInfoProviderClass.newInstance();
64+
} catch (ClassNotFoundException e) {
65+
// ReactModuleSpecProcessor does not run at build-time. Create this ReactModuleInfoProvider by
66+
// hand.
67+
return new ReactModuleInfoProvider() {
68+
@Override
69+
public Map<String, ReactModuleInfo> getReactModuleInfos() {
70+
final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
71+
72+
Class<? extends NativeModule>[] moduleList =
73+
new Class[] {
74+
RNPermissionsModule.class,
75+
};
76+
77+
for (Class<? extends NativeModule> moduleClass : moduleList) {
78+
ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
79+
80+
reactModuleInfoMap.put(
81+
reactModule.name(),
82+
new ReactModuleInfo(
83+
reactModule.name(),
84+
moduleClass.getName(),
85+
reactModule.canOverrideExistingModule(),
86+
reactModule.needsEagerInit(),
87+
reactModule.hasConstants(),
88+
reactModule.isCxxModule(),
89+
TurboModule.class.isAssignableFrom(moduleClass)));
90+
}
91+
92+
return reactModuleInfoMap;
93+
}
94+
};
95+
} catch (InstantiationException | IllegalAccessException e) {
96+
throw new RuntimeException(
97+
"No ReactModuleInfoProvider for com.zoontek.rnpermissions.RNPermissionsPackage$$ReactModuleInfoProvider", e);
98+
}
99+
}
12100

13101
@Override
14102
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
2+
/**
3+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
4+
*
5+
* Do not edit this file as changes may cause incorrect behavior and will be lost
6+
* once the code is regenerated.
7+
*
8+
* @generated by codegen project: GenerateModuleJavaSpec.js
9+
*
10+
* @nolint
11+
*/
12+
13+
package com.zoontek.rnpermissions;
14+
15+
import com.facebook.proguard.annotations.DoNotStrip;
16+
import com.facebook.react.bridge.Promise;
17+
import com.facebook.react.bridge.ReactApplicationContext;
18+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
19+
import com.facebook.react.bridge.ReactMethod;
20+
import com.facebook.react.bridge.ReactModuleWithSpec;
21+
import com.facebook.react.bridge.ReadableArray;
22+
import com.facebook.react.common.build.ReactBuildConfig;
23+
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
24+
import java.util.Arrays;
25+
import java.util.HashSet;
26+
import java.util.Map;
27+
import java.util.Set;
28+
import javax.annotation.Nullable;
29+
30+
public abstract class NativePermissionsModuleSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule {
31+
public NativePermissionsModuleSpec(ReactApplicationContext reactContext) {
32+
super(reactContext);
33+
}
34+
35+
@ReactMethod
36+
@DoNotStrip
37+
public abstract void openSettings(Promise promise);
38+
39+
@ReactMethod
40+
@DoNotStrip
41+
public abstract void checkNotifications(Promise promise);
42+
43+
@ReactMethod
44+
@DoNotStrip
45+
public abstract void checkPermission(String permission, Promise promise);
46+
47+
@ReactMethod
48+
@DoNotStrip
49+
public abstract void shouldShowRequestPermissionRationale(String permission, Promise promise);
50+
51+
@ReactMethod
52+
@DoNotStrip
53+
public abstract void requestPermission(String permission, Promise promise);
54+
55+
@ReactMethod
56+
@DoNotStrip
57+
public abstract void checkMultiplePermissions(ReadableArray permissions, Promise promise);
58+
59+
@ReactMethod
60+
@DoNotStrip
61+
public abstract void requestMultiplePermissions(ReadableArray permissions, Promise promise);
62+
63+
@ReactMethod
64+
@DoNotStrip
65+
public abstract void check(double permission, Promise promise);
66+
67+
@ReactMethod
68+
@DoNotStrip
69+
public abstract void checkLocationAccuracy(Promise promise);
70+
71+
@ReactMethod
72+
@DoNotStrip
73+
public abstract void request(double permission, Promise promise);
74+
75+
@ReactMethod
76+
@DoNotStrip
77+
public abstract void requestLocationAccuracy(String purposeKey, Promise promise);
78+
79+
@ReactMethod
80+
@DoNotStrip
81+
public abstract void requestNotifications(ReadableArray options, Promise promise);
82+
83+
@ReactMethod
84+
@DoNotStrip
85+
public abstract void openLimitedPhotoLibraryPicker(Promise promise);
86+
87+
protected abstract Map<String, Object> getTypedExportedConstants();
88+
89+
@Override
90+
@DoNotStrip
91+
public final @Nullable Map<String, Object> getConstants() {
92+
Map<String, Object> constants = getTypedExportedConstants();
93+
if (ReactBuildConfig.DEBUG || ReactBuildConfig.IS_INTERNAL_BUILD) {
94+
Set<String> obligatoryFlowConstants = new HashSet<>(Arrays.asList(
95+
"available"
96+
));
97+
Set<String> optionalFlowConstants = new HashSet<>();
98+
Set<String> undeclaredConstants = new HashSet<>(constants.keySet());
99+
undeclaredConstants.removeAll(obligatoryFlowConstants);
100+
undeclaredConstants.removeAll(optionalFlowConstants);
101+
if (!undeclaredConstants.isEmpty()) {
102+
throw new IllegalStateException(String.format("Native Module Flow doesn't declare constants: %s", undeclaredConstants));
103+
}
104+
undeclaredConstants = obligatoryFlowConstants;
105+
undeclaredConstants.removeAll(constants.keySet());
106+
if (!undeclaredConstants.isEmpty()) {
107+
throw new IllegalStateException(String.format("Native Module doesn't fill in constants: %s", undeclaredConstants));
108+
}
109+
}
110+
return constants;
111+
}
112+
}

0 commit comments

Comments
 (0)