Browse Source

added android support 🎉🎉

Yonah Forst 8 years ago
parent
commit
eb87369148

+ 90
- 10
README.md View File

2
 Request user permissions from React Native (iOS only - android coming soon)
2
 Request user permissions from React Native (iOS only - android coming soon)
3
 
3
 
4
 The current supported permissions are:
4
 The current supported permissions are:
5
-- Push Notifications
6
 - Location
5
 - Location
7
 - Camera
6
 - Camera
8
 - Microhone
7
 - Microhone
9
 - Photos
8
 - Photos
10
 - Contacts
9
 - Contacts
11
 - Events
10
 - Events
12
-- Reminders
13
-- Bluetooth (Peripheral role. Don't use for Central only)
14
-- Background Refresh
11
+- Reminders *(iOS only)*
12
+- Bluetooth *(iOS only)*
13
+- Push Notifications *(iOS only)*
14
+- Background Refresh *(iOS only)*
15
 
15
 
16
 ##General Usage
16
 ##General Usage
17
 ```js
17
 ```js
72
 
72
 
73
 ##API
73
 ##API
74
 
74
 
75
-_Permission statuses_ - `authorized`, `denied`, `restricted`, or `undetermined`
75
+###Permission statuses
76
+Promises resolve into one of these statuses
76
 
77
 
77
-_Permission types_ - `location`, `camera`, `microphone`, `photo`, `contacts`, `event`, `reminder`, `bluetooth`, `notification`, or `backgroundRefresh`
78
+| Return value | Notes|
79
+|---|---|
80
+|`authorized`| user has authorized this permission |
81
+|`denied`| user has denied permissions at least once. On iOS this means that the user will not be prompted again. Android users can be promted multiple times until they select 'Never ask me again'|
82
+|`restricted`| iOS only|
83
+|`undetermined`| user has not yet been prompted with a permission dialog |
78
 
84
 
85
+###Supported permission types
86
+
87
+| Name | iOS | Android |
88
+|---|---|---|
89
+|`location`| ✔️ | ✔ |
90
+|`camera`| ✔️ | ✔ |
91
+|`microphone`| ✔️ | ✔ |
92
+|`photo`| ✔️ | ✔ |
93
+|`contacts`| ✔️ | ✔ |
94
+|`event`| ✔️ | ✔ |
95
+|`bluetooth`| ✔️ | ❌ |
96
+|`reminder`| ✔️ | ❌ |
97
+|`notification`| ✔️ | ❌ |
98
+|`backgroundRefresh`| ✔️ | ❌ |
99
+
100
+###Methods
79
 | Method Name | Arguments | Notes
101
 | Method Name | Arguments | Notes
80
 |---|---|---|
102
 |---|---|---|
81
 | `getPermissionStatus` | `type` | - Returns a promise with the permission status. Note: for type `location`, iOS `AuthorizedAlways` and `AuthorizedWhenInUse` both return `authorized` |
103
 | `getPermissionStatus` | `type` | - Returns a promise with the permission status. Note: for type `location`, iOS `AuthorizedAlways` and `AuthorizedWhenInUse` both return `authorized` |
85
 | `openSettings` | *none* | - Switches the user to the settings page of your app (iOS 8.0 and later)  |
107
 | `openSettings` | *none* | - Switches the user to the settings page of your app (iOS 8.0 and later)  |
86
 | `canOpenSettings` | *none* | - Returns a boolean indicating if the device supports switching to the settings page |
108
 | `canOpenSettings` | *none* | - Returns a boolean indicating if the device supports switching to the settings page |
87
 
109
 
88
-Note: Permission type `bluetooth` represents the status of the `CBPeripheralManager`. Don't use this if you're only using `CBCentralManager`
89
-
90
-###Special cases
110
+###iOS Notes
111
+Permission type `bluetooth` represents the status of the `CBPeripheralManager`. Don't use this if only need `CBCentralManager`
91
 
112
 
92
 `requestPermission` also accepts a second parameter for types `location` and `notification`.
113
 `requestPermission` also accepts a second parameter for types `location` and `notification`.
93
 - `location`: the second parameter is a string, either `always` or `whenInUse`(default).
114
 - `location`: the second parameter is a string, either `always` or `whenInUse`(default).
105
       })
126
       })
106
 ```
127
 ```
107
 
128
 
129
+###Android Notes
130
+All required permissions also need to be included in the Manifest before they can be requested. Otherwise `requestPermission` will immediately return `denied`.
131
+
132
+Permissions are automatically accepted for targetSdkVersion < 23 but you can still use `getPermissionStatus` to check if the user has disabled them from Settings.
133
+
134
+Here's a map of types to Android system permissions names:  
135
+`location` -> `android.permission.ACCESS_FINE_LOCATION`  
136
+`camera` -> `android.permission.CAMERA`  
137
+`microphone` -> `android.permission.RECORD_AUDIO`  
138
+`photo` -> `android.permission.READ_EXTERNAL_STORAGE`  
139
+`contacts` -> `android.permission.READ_CONTACTS`  
140
+`event` -> `android.permission.READ_CALENDAR`  
141
+
142
+You can request write access to any of these types by also including the appropriate write permission in the Manifest. Read more here: https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
108
 
143
 
109
 ##Setup
144
 ##Setup
110
 
145
 
111
 ````
146
 ````
112
 npm install --save react-native-permissions
147
 npm install --save react-native-permissions
148
+rnpm link
113
 ````
149
 ````
114
 
150
 
115
-##iOS
151
+###Or manualy linking   
152
+
153
+####iOS
116
 * Run open node_modules/react-native-permissions
154
 * Run open node_modules/react-native-permissions
117
 * Drag ReactNativePermissions.xcodeproj into the Libraries group of your app's Xcode project
155
 * Drag ReactNativePermissions.xcodeproj into the Libraries group of your app's Xcode project
118
 * Add libReactNativePermissions.a to `Build Phases -> Link Binary With Libraries.
156
 * Add libReactNativePermissions.a to `Build Phases -> Link Binary With Libraries.
157
+
158
+####Android
159
+#####Step 1 - Update Gradle Settings
160
+
161
+```
162
+// file: android/settings.gradle
163
+...
164
+
165
+include ':react-native-permissions'
166
+project(':react-native-permissions').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-permissions/android')
167
+```
168
+#####Step 2 - Update Gradle Build
169
+
170
+```
171
+// file: android/app/build.gradle
172
+...
173
+
174
+dependencies {
175
+    ...
176
+    compile project(':react-native-permissions')
177
+}
178
+```
179
+#####Step 3 - Register React Package
180
+```
181
+...
182
+import com.joshblour.reactnativepermissions.ReactNativePermissionsPackage; // <--- import
183
+
184
+public class MainActivity extends ReactActivity {
185
+
186
+    ...
187
+
188
+    @Override
189
+    protected List<ReactPackage> getPackages() {
190
+        return Arrays.<ReactPackage>asList(
191
+            new MainReactPackage(),
192
+            new ReactNativePermissionsPackage() // <------ add the package
193
+        );
194
+    }
195
+
196
+    ...
197
+}
198
+```

+ 0
- 3
ReactNativePermissions.android.js View File

1
-'use strict';
2
-
3
-module.exports = {};

ReactNativePermissions.ios.js → ReactNativePermissions.js View File

1
 'use strict';
1
 'use strict';
2
 
2
 
3
-var React = require('react-native');
4
-var RNPermissions = React.NativeModules.ReactNativePermissions;
3
+var ReactNative = require('react-native')
4
+var Platform = ReactNative.Platform
5
+var RNPermissions = ReactNative.NativeModules.ReactNativePermissions;
5
 
6
 
6
-const RNPTypes = [
7
-	'location',
8
-	'camera',
9
-	'microphone',
10
-	'photo',
11
-	'contacts',
12
-	'event',
13
-	'reminder',
14
-	'bluetooth',
15
-	'notification',
16
-	'backgroundRefresh', 
17
-]
7
+const RNPTypes = {
8
+	ios: [
9
+		'location',
10
+		'camera',
11
+		'microphone',
12
+		'photo',
13
+		'contacts',
14
+		'event',
15
+		'reminder',
16
+		'bluetooth',
17
+		'notification',
18
+		'backgroundRefresh', 
19
+	],
20
+	android: [
21
+		'location',
22
+		'camera',
23
+		'microphone',
24
+		'contacts',
25
+		'event',
26
+		'photos',
27
+	]
28
+}
18
 
29
 
19
 class ReactNativePermissions {
30
 class ReactNativePermissions {
20
 	constructor() {
31
 	constructor() {
24
 		this.StatusAuthorized = 'authorized'
35
 		this.StatusAuthorized = 'authorized'
25
 		this.StatusRestricted = 'restricted'
36
 		this.StatusRestricted = 'restricted'
26
 
37
 
27
-		RNPTypes.forEach(type => {
38
+		this.getPermissionTypes().forEach(type => {
28
 			let methodName = `${type}PermissionStatus`
39
 			let methodName = `${type}PermissionStatus`
29
 			this[methodName] = p => {
40
 			this[methodName] = p => {
30
 				console.warn(`ReactNativePermissions: ${methodName} is depricated. Use getPermissionStatus('${type}') instead.`)
41
 				console.warn(`ReactNativePermissions: ${methodName} is depricated. Use getPermissionStatus('${type}') instead.`)
42
 	}
53
 	}
43
 
54
 
44
 	getPermissionTypes() {
55
 	getPermissionTypes() {
45
-		return RNPTypes;
56
+		return RNPTypes[Platform.OS];
46
 	}
57
 	}
47
 
58
 
48
 	getPermissionStatus(permission) {
59
 	getPermissionStatus(permission) {
49
-		if (RNPTypes.includes(permission)) {
60
+		if (this.getPermissionTypes().includes(permission)) {
50
 			return RNPermissions.getPermissionStatus(permission)
61
 			return RNPermissions.getPermissionStatus(permission)
51
 		} else {
62
 		} else {
52
-			return Promise.reject(`ReactNativePermissions: ${permission} is not a valid permission type`)
63
+			return Promise.reject(`ReactNativePermissions: ${permission} is not a valid permission type on ${Platform.OS}`)
53
 		}
64
 		}
54
 	}
65
 	}
55
 
66
 
56
 	requestPermission(permission, type) {
67
 	requestPermission(permission, type) {
57
-		switch (permission) {
58
-			case "location":
59
-				return RNPermissions.requestLocation(type || 'always')
60
-			case "camera":
61
-				return RNPermissions.requestCamera();
62
-			case "microphone":
63
-				return RNPermissions.requestMicrophone();
64
-			case "photo":
65
-				return RNPermissions.requestPhoto();
66
-			case "contacts":
67
-				return RNPermissions.requestContacts();
68
-			case "event":
69
-				return RNPermissions.requestEvent();
70
-			case "reminder":
71
-				return RNPermissions.requestReminder();
72
-			case "bluetooth":
73
-				return RNPermissions.requestBluetooth();
74
-			case "notification":
75
-				return RNPermissions.requestNotification(type || ['alert', 'badge', 'sound'])
76
-			case "backgroundRefresh":
77
-				return Promise.reject('You cannot request backgroundRefresh')
78
-			default:
79
-				return Promise.reject('invalid type: ' + type)
68
+		let options; 
69
+
70
+		if (!this.getPermissionTypes().includes(permission)) {
71
+			return Promise.reject(`ReactNativePermissions: ${permission} is not a valid permission type on ${Platform.OS}`)
72
+		} else if (permission == 'backgroundRefresh'){
73
+			return Promise.reject('You cannot request backgroundRefresh')
74
+		} else if (permission == 'location') {
75
+			options = type || 'always'
76
+		} else if (permission == 'notification') {
77
+			options = type || ['alert', 'badge', 'sound']
80
 		}
78
 		}
79
+
80
+		return RNPermissions.requestPermission(permission, options)
81
 	}
81
 	}
82
 
82
 
83
 	//recursive funciton to chain a promises for a list of permissions
83
 	//recursive funciton to chain a promises for a list of permissions

+ 38
- 35
ReactNativePermissions.m View File

109
     resolve(status);
109
     resolve(status);
110
 }
110
 }
111
 
111
 
112
-RCT_REMAP_METHOD(requestLocation, requestLocation:(NSString *)type resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
112
+RCT_REMAP_METHOD(requestPermission, permissionType:(RNPType)type json:(id)json resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
113
+{
114
+    NSString *status;
115
+    
116
+    switch (type) {
117
+        case RNPTypeLocation:
118
+            return [self requestLocation:json resolve:resolve];
119
+        case RNPTypeCamera:
120
+            return [RNPAudioVideo request:@"video" completionHandler:resolve];
121
+        case RNPTypeMicrophone:
122
+            return [RNPAudioVideo request:@"audio" completionHandler:resolve];
123
+        case RNPTypePhoto:
124
+            return [RNPPhoto request:resolve];
125
+        case RNPTypeContacts:
126
+            return [RNPContacts request:resolve];
127
+        case RNPTypeEvent:
128
+            return [RNPEvent request:@"event" completionHandler:resolve];
129
+        case RNPTypeReminder:
130
+            return [RNPEvent request:@"reminder" completionHandler:resolve];
131
+        case RNPTypeBluetooth:
132
+            return [self requestBluetooth:resolve];
133
+        case RNPTypeNotification:
134
+            return [self requestNotification:json resolve:resolve];
135
+        default:
136
+            break;
137
+    }
138
+    
139
+
140
+}
141
+
142
+
143
+- (void) requestLocation:(id)json resolve:(RCTPromiseResolveBlock)resolve
113
 {
144
 {
114
     if (self.locationMgr == nil) {
145
     if (self.locationMgr == nil) {
115
         self.locationMgr = [[RNPLocation alloc] init];
146
         self.locationMgr = [[RNPLocation alloc] init];
116
     }
147
     }
117
     
148
     
149
+    NSString *type = [RCTConvert NSString:json];
150
+    
118
     [self.locationMgr request:type completionHandler:resolve];
151
     [self.locationMgr request:type completionHandler:resolve];
119
 }
152
 }
120
 
153
 
121
-RCT_REMAP_METHOD(requestNotification, requestNotification:(NSArray *)typeStrings resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
154
+- (void) requestNotification:(id)json resolve:(RCTPromiseResolveBlock)resolve
122
 {
155
 {
156
+    NSArray *typeStrings = [RCTConvert NSArray:json];
157
+    
123
     UIUserNotificationType types;
158
     UIUserNotificationType types;
124
     if ([typeStrings containsObject:@"alert"])
159
     if ([typeStrings containsObject:@"alert"])
125
         types = types | UIUserNotificationTypeAlert;
160
         types = types | UIUserNotificationTypeAlert;
140
 }
175
 }
141
 
176
 
142
 
177
 
143
-RCT_REMAP_METHOD(requestBluetooth, requestBluetooth:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
178
+- (void) requestBluetooth:(RCTPromiseResolveBlock)resolve
144
 {
179
 {
145
     if (self.bluetoothMgr == nil) {
180
     if (self.bluetoothMgr == nil) {
146
         self.bluetoothMgr = [[RNPBluetooth alloc] init];
181
         self.bluetoothMgr = [[RNPBluetooth alloc] init];
149
     [self.bluetoothMgr request:resolve];
184
     [self.bluetoothMgr request:resolve];
150
 }
185
 }
151
 
186
 
152
-RCT_REMAP_METHOD(requestCamera, requestCamera:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
153
-{
154
-    [RNPAudioVideo request:@"video" completionHandler:resolve];
155
-}
156
-
157
-RCT_REMAP_METHOD(requestMicrophone, requestMicrophone:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
158
-{
159
-    [RNPAudioVideo request:@"audio" completionHandler:resolve];
160
-}
161
-
162
-RCT_REMAP_METHOD(requestEvent, requestEvents:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
163
-{
164
-    [RNPEvent request:@"event" completionHandler:resolve];
165
-}
166
-
167
-RCT_REMAP_METHOD(requestReminder, requestReminders:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
168
-{
169
-    [RNPEvent request:@"reminder" completionHandler:resolve];
170
-}
171
-
172
-RCT_REMAP_METHOD(requestPhoto, requestPhoto:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
173
-{
174
-    [RNPPhoto request:resolve];
175
-}
176
-
177
-RCT_REMAP_METHOD(requestContacts, requestContacts:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
178
-{
179
-    [RNPContacts request:resolve];
180
-}
181
-
182
-
183
-
184
 
187
 
185
 
188
 
186
 
189
 

+ 34
- 0
android/build.gradle View File

1
+buildscript {
2
+    repositories {
3
+        jcenter()
4
+    }
5
+
6
+    dependencies {
7
+        classpath 'com.android.tools.build:gradle:2.1.+'
8
+    }
9
+}
10
+
11
+apply plugin: 'com.android.library'
12
+
13
+android {
14
+    compileSdkVersion 23
15
+    buildToolsVersion "23.0.1"
16
+
17
+    defaultConfig {
18
+        minSdkVersion 18
19
+        targetSdkVersion 23
20
+        versionCode 1
21
+        versionName "1.0"
22
+    }
23
+    lintOptions {
24
+        abortOnError false
25
+    }
26
+}
27
+
28
+repositories {
29
+    jcenter()
30
+}
31
+
32
+dependencies {
33
+    compile 'com.facebook.react:react-native:+'
34
+}

+ 3
- 0
android/src/main/AndroidManifest.xml View File

1
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+    package="com.joshblour.reactnativepermissions">
3
+</manifest>

+ 128
- 0
android/src/main/java/com/joshblour/reactnativepermissions/ReactNativePermissionsModule.java View File

1
+package com.joshblour.reactnativepermissions;
2
+
3
+import android.Manifest;
4
+import android.content.Intent;
5
+import android.net.Uri;
6
+import android.provider.Settings;
7
+import android.support.v4.app.ActivityCompat;
8
+import android.support.v4.content.ContextCompat;
9
+import android.support.v4.content.PermissionChecker;
10
+
11
+import com.facebook.react.bridge.Callback;
12
+import com.facebook.react.bridge.Promise;
13
+import com.facebook.react.bridge.ReactApplicationContext;
14
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
15
+import com.facebook.react.bridge.ReactMethod;
16
+import com.facebook.react.bridge.ReadableMap;
17
+import com.facebook.react.bridge.ReadableArray;
18
+import com.facebook.react.modules.permissions.PermissionsModule;
19
+
20
+
21
+public class ReactNativePermissionsModule extends ReactContextBaseJavaModule {
22
+  private final ReactApplicationContext reactContext;
23
+  private final PermissionsModule mPermissionsModule;
24
+
25
+  public enum RNType {
26
+    LOCATION,
27
+    CAMERA,
28
+    MICROPHONE,
29
+    CONTACTS,
30
+    EVENT,
31
+    PHOTOS;
32
+  }
33
+
34
+  public ReactNativePermissionsModule(ReactApplicationContext reactContext) {
35
+    super(reactContext);
36
+    this.reactContext = reactContext;
37
+    mPermissionsModule = new PermissionsModule(this.reactContext);
38
+  }
39
+
40
+  @Override
41
+  public String getName() {
42
+    return "ReactNativePermissions";
43
+  }
44
+
45
+  @ReactMethod
46
+  public void getPermissionStatus(String permissionString, Promise promise) {
47
+    String permission = permissionForString(permissionString);
48
+
49
+    // check if permission is valid
50
+    if (permission == null) {
51
+      promise.reject("unknown-permission", "ReactNativePermissions: unknown permission type - " + permissionString);
52
+      return;
53
+    }
54
+
55
+    int result = PermissionChecker.checkSelfPermission(this.reactContext, permission);
56
+    switch (result) {
57
+      case PermissionChecker.PERMISSION_DENIED:
58
+        // PermissionDenied could also mean that we've never asked for permission yet.
59
+        // Use shouldShowRequestPermissionRationale to determined which on it is.
60
+        if (getCurrentActivity() != null) {
61
+          boolean deniedOnce = ActivityCompat.shouldShowRequestPermissionRationale(getCurrentActivity(), permission);
62
+          promise.resolve(deniedOnce ? "denied" : "undetermined");
63
+        } else {
64
+          promise.resolve("denied");
65
+        }
66
+        break;
67
+      case PermissionChecker.PERMISSION_DENIED_APP_OP:
68
+        promise.resolve("denied");
69
+        break;
70
+      case PermissionChecker.PERMISSION_GRANTED:
71
+        promise.resolve("authorized");
72
+        break;
73
+      default:
74
+        promise.resolve("undetermined");
75
+        break;
76
+    }
77
+  }
78
+
79
+  @ReactMethod
80
+  public void requestPermission(final String permissionString, String nullForiOSCompat, final Promise promise) {
81
+    String permission = permissionForString(permissionString);
82
+    mPermissionsModule.requestPermission(permission, new Callback() {
83
+      @Override
84
+      public void invoke(Object... args) {
85
+        getPermissionStatus(permissionString, promise);
86
+//        promise.resolve((boolean)args[1] ? "authorized" : "denied");
87
+      }
88
+    }, null);
89
+  }
90
+
91
+
92
+  @ReactMethod
93
+  public void canOpenSettings(Promise promise) {
94
+    promise.resolve(true);
95
+  }
96
+
97
+  @ReactMethod
98
+  public void openSettings() {
99
+    final Intent i = new Intent();
100
+    i.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
101
+    i.addCategory(Intent.CATEGORY_DEFAULT);
102
+    i.setData(Uri.parse("package:" + this.reactContext.getPackageName()));
103
+    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
104
+    i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
105
+    i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
106
+    this.reactContext.startActivity(i);
107
+  }
108
+
109
+  private String permissionForString(String permission) {
110
+    switch (RNType.valueOf(permission.toUpperCase())) {
111
+      case LOCATION:
112
+        return Manifest.permission.ACCESS_FINE_LOCATION;
113
+      case CAMERA:
114
+        return Manifest.permission.CAMERA;
115
+      case MICROPHONE:
116
+        return Manifest.permission.RECORD_AUDIO;
117
+      case CONTACTS:
118
+        return Manifest.permission.READ_CONTACTS;
119
+      case EVENT:
120
+        return Manifest.permission.READ_CALENDAR;
121
+      case PHOTOS:
122
+        return Manifest.permission.READ_EXTERNAL_STORAGE;
123
+      default:
124
+        return null;
125
+    }
126
+  }
127
+
128
+}

+ 28
- 0
android/src/main/java/com/joshblour/reactnativepermissions/ReactNativePermissionsPackage.java View File

1
+package com.joshblour.reactnativepermissions;
2
+
3
+import java.util.Arrays;
4
+import java.util.Collections;
5
+import java.util.List;
6
+
7
+import com.facebook.react.ReactPackage;
8
+import com.facebook.react.bridge.NativeModule;
9
+import com.facebook.react.bridge.ReactApplicationContext;
10
+import com.facebook.react.uimanager.ViewManager;
11
+import com.facebook.react.bridge.JavaScriptModule;
12
+
13
+public class ReactNativePermissionsPackage implements ReactPackage {
14
+    @Override
15
+    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
16
+      return Arrays.<NativeModule>asList(new ReactNativePermissionsModule(reactContext));
17
+    }
18
+
19
+    @Override
20
+    public List<Class<? extends JavaScriptModule>> createJSModules() {
21
+      return Collections.emptyList();
22
+    }
23
+
24
+    @Override
25
+    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
26
+      return Collections.emptyList();
27
+    }
28
+}