How to create custom plugin in React Native? That should call native Java code asking for Camera and Recording permissions.

Ankit Maheshwari
7 min readJun 17, 2024

--

Creating a custom plugin in React Native to call native Java code, specifically for handling camera and recording permissions, involves several steps. You’ll need to set up both the JavaScript and native Android sides to communicate with each other. Below are the detailed steps for achieving this:

Step-by-Step Guide

1. Setting Up React Native Project

First, ensure you have a React Native project set up. If you don’t have one, you can create a new project using:

sudo npx react-native init MyProject
Run instructions for Android:
• Have an Android emulator running (quickest way to get started), or a device connected.
• cd "/Users/ankitmaheshwari/Data/React-Projects/POC/MyProject" && npx react-native run-android

Run instructions for iOS:
• cd "/Users/ankitmaheshwari/Data/React-Projects/POC/MyProject/ios"

• Install Cocoapods
• bundle install # you need to run this only once in your project.
• bundle exec pod install
• cd ..

• npx react-native run-ios
- or -
• Open MyProject/ios/MyProject.xcodeproj in Xcode or run "xed -b ios"
• Hit the Run button

2. Create a Custom Native Module in Android

A. Create Java Class for Native Module:

Navigate to your Android project directory and create a new Java class in android/app/src/main/java/com/yourprojectname/.

cd android/app/src/main/java/com/yourprojectname/
mkdir custommodule
touch CustomModule.java

Edit the CustomModule.java to create a basic native module:

package com.yourprojectname.custommodule;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;

public class CustomModule extends ReactContextBaseJavaModule {
private static final int CAMERA_PERMISSION_CODE = 100;
private static final int RECORD_AUDIO_PERMISSION_CODE = 101;

CustomModule(ReactApplicationContext context) {
super(context);
}

@NonNull
@Override
public String getName() {
return "CustomModule";
}

@ReactMethod
public void checkPermissions(Promise promise) {
boolean cameraGranted = ContextCompat.checkSelfPermission(getReactApplicationContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
boolean recordAudioGranted = ContextCompat.checkSelfPermission(getReactApplicationContext(), Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED;
promise.resolve(cameraGranted && recordAudioGranted);
}

@ReactMethod
public void requestPermissions(Promise promise) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, CAMERA_PERMISSION_CODE);
} else {
promise.resolve(true);
}
}
}

B. Register the Module:

Create a CustomPackage.java to register your module:

sudo touch CustomPackage.java

Code inside CustomPackage.java

package com.yourprojectname.custommodule;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CustomPackage implements ReactPackage {

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new CustomModule(reactContext));
return modules;
}

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

C. Register Your Package in MainApplication.java:

Edit android/app/src/main/java/com/yourprojectname/MainApplication.java to include your new package:

import com.myproject.custommodule.CustomPackage;

@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
packages.add(new CustomPackage());
return packages;
}

3. Create JavaScript Interface

A. Create a JS or TSX Module to Access the Native Code:

Create a new JavaScript file in your React Native project, for example, CustomModule.js.

import { NativeModules } from 'react-native';

const { CustomModule } = NativeModules;

export const checkPermissions = () => {
return CustomModule.checkPermissions();
};

export const requestPermissions = () => {
return CustomModule.requestPermissions();
};

If you are using tsx version of React Native project use CustomModule.tsx code as follows:

import { NativeModules } from 'react-native';

// Type definition for the `CustomModule` object
interface CustomModule {
checkPermissions(): Promise<boolean>; // Return type is a promise that resolves to a boolean
requestPermissions(): Promise<void>; // Return type is a promise that resolves to nothing (void)
}

// Cast the NativeModules.CustomModule to the typed interface
const CustomModule = NativeModules.CustomModule as CustomModule;

export const checkPermissions = (): Promise<boolean> => {
return CustomModule.checkPermissions();
};

export const requestPermissions = (): Promise<void> => {
return CustomModule.requestPermissions();
};

This is how I created under /src/modules/CustomModules.tsx

B. Use the Module in Your React Native Component:

You can now use this module in your React Native components. For instance, in App.tsx:

import React, { useEffect } from 'react';
import { View, Button, Alert } from 'react-native';
import { checkPermissions, requestPermissions } from './CustomModule';

const App = () => {
useEffect(() => {
checkPermissions()
.then((granted: boolean) => {
if (!granted) {
Alert.alert('Permissions not granted', 'Camera and Audio permissions are required.');
}
});
}, []);

const requestCameraAndAudioPermissions = () => {
requestPermissions()
.then((granted: boolean) => {
if (granted) {
Alert.alert('Permissions granted', 'You can now access the camera and record audio.');
} else {
Alert.alert('Permissions not granted', 'Camera and Audio permissions are required.');
}
});
};

return (
<View>
<Button title="Request Permissions" onPress={requestCameraAndAudioPermissions} />
</View>
);
};

export default App;

C. Handling Permissions in Native Code

Make sure you handle the results of permission requests in your Java code. Here’s how you can handle it in MainActivity.java:

import android.content.pm.PackageManager;
import androidx.annotation.NonNull;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 100: // CAMERA_PERMISSION_CODE
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted
} else {
// Permission denied
}
break;
case 101: // RECORD_AUDIO_PERMISSION_CODE
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted
} else {
// Permission denied
}
break;
}
}

After adding above code, the final code in MainActivity.java:

package com.myproject

import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate

import android.content.pm.PackageManager;
import androidx.annotation.NonNull;

class MainActivity : ReactActivity() {

/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
override fun getMainComponentName(): String = "MyProject"

/**
* Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
* which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
*/
override fun createReactActivityDelegate(): ReactActivityDelegate =
DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)


/**
* Handling Permissions in Native Code: handling the CustomModule results of permission requests
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 100: // CAMERA_PERMISSION_CODE
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted
} else {
// Permission denied
}
break;
case 101: // RECORD_AUDIO_PERMISSION_CODE
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted
} else {
// Permission denied
}
break;
}
}

}

D. Add Permissions to AndroidManifest.xml

Make sure to add the required permissions to your AndroidManifest.xml file:

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>

Conclusion

By following these steps, you can create a custom React Native plugin to call native Java code for handling camera and recording permissions. This approach allows you to harness the capabilities of the native platform while using React Native for cross-platform development.

Use Auto-Linking (React Native 0.60 and Above)

1. Install Dependencies:

npm install [package-name]

Or just use npm install to install all dependencies in package.json file.

2. Run React Native:

npx react-native run-android
npx react-native run-ios

For iOS: Navigate to the ios directory and run pod install to install the necessary CocoaPods dependencies:

cd ios
pod install
cd ..

Known error while running Android build: npx react-native run-android

SDK location not found. Define a valid SDK location with an ANDROID_HOME environment variable or by setting the sdk.dir path in your project’s local properties file at ‘/Users/ankitmaheshwari/Data/React-Projects/POC/MyProject/android/local.properties’.

To know Android_Home path:

Command to know Android_Home path

To fix this, update local.properties File:

If environment variables are less familiar, you can specify the SDK location directly in your project:

  • Navigate to the android/local.properties file in your project directory.
  • If it doesn’t exist, create a new file named local.properties.
  • Add the line sdk.dir=/path/to/android/sdk (replace /path/to/android/sdk with the actual path).
  • Save the local.properties file.

Restart Terminal:

After making changes to environment variables, restart your terminal window for the changes to take effect.

Verify Installation:

Once you’ve set ANDROID_HOME or updated local.properties, try building your React Native app again.

--

--

Ankit Maheshwari

Frontend/Backend Engineer (Angular, React Native, NodeJS, MongoDB)