Creating a surrounding light sensor module (using Native Module) for Android device in React Native
A couple of days ago, I was working on a task in a React Native app where I need to automatically turn on the torch/flash light of the device when the user turns on the camera within the app and the surrounding environment has dim/dark light. The flash/torch light should be auto turned off again if the surrounding light becomes significantly brighter.
Naturally, I looked for some package in react native which can provide me some data about my surrounding’s light condition. Unfortunately, I did not get any such package/library which provides such functionality. So I did some research about this topic and came across SensorManager API for android. Basically SensorManager lets you access the device’s sensors
.
Checkout the SensorManager API docs — Docs Link
The hurdle here is that I can get the surrounding light data from the SensorManager but I need this value in my React Native JavaScript end so that I can turn on/off the flash light off the device based on the value.
Naturally, I had to write my own Native Module for android to perform this job. If you are new to Native Modules in React Native, check out their docs — Native Modules Intro . In short, sometimes as a developer we want to access some Native APIs of the device which are not available in Javascript. From their docs —
The NativeModule system exposes instances of Java/Objective-C/C++ (native) classes to JavaScript (JS) as JS objects, thereby allowing you to execute arbitrary native code from within JS.
I will list down the approach I have used —
- Create a Native Module which will implement the SensorEventListener interface. This interface provides two methods
onSensoryChanged(SensorEvent event)
andonAccuracyChanged(Sensor sensor, int accuracy)
, which we need to override in our module. - Create a method which will send some kind of event with the current surroundings light value to the Javascript end.
- Register a listener at Javascript side which will listen to the value changes to the device’s surrounding environment.
- Lastly, turn on/off the device’s torch light based on the light value we receive from the listener.
Step 1 and Step 2 —
First, create the LightSensorModule. Inside android/app/src/main/java/com/<yourappname>
, create a file called LightSensorModule.java
(you can name it whatever you want).
What it does —
a. It implements SensorEventListener
interface and overrides its two methods onSensorChanged
and onAccuracyChanged
. We are interested in the method onSensorChanged
which will give us the value whenever the sensor value changes.
b. In the constructor of our module, we are initializing our SensorManager API of TYPE_LIGHT
for our case. Check out all the type of sensors present here.
c. We are creating two methods called startLightSensor
and stopLightSensor
which we will be invoking from our JavaScript side. startLightSensor
registers a listener for the sensor manager of the device. stopLightSensor
unregisters/destroys the invoked listener.
d. onSensorChanged
overridden method receives the sensor event value in its param, inside it we are creating a map with key lightValue
and storing the corresponding sensor event value with it.
e. In our last process, we are passing the map to a method called sendEvent
which uses the DeviceEventManagerModule to send events with some event properties. This is the event for which we will listen to at our Javascript end and ultimately fetch the lightValue
from it.
Next, we have to create a package. Inside android/app/src/main/java/com/<yourappname>
, create a file called LightSensorPackage.java
where we are creating our NativeModules and declaring which module we need to add (in our case LightSensorModule
which we just created above)
For the last process in this step, to register the LightSensorModule
package, you must add LightSensorPackage
to the list of packages returned in ReactNativeHost's getPackages()
method. Open up your MainApplication.java
or MainApplication.kt
file, which can be found in the following path: android/app/src/main/java/com/your-app-name/
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings(“UnnecessaryLocalVariable”)
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new LightSensorPackage());
return packages;
}
This ends our codes for native side. We now need to invoke these created methods in our JavaScript end.
Step 3 and 4-
Here, we are first starting our light sensor on the mount of the component inside the useEffect
hook and also listening to the event with the name LightSensor
which we are emitting from our native side code. The event value will be an object with a key lightValue
that has the actual value of light (in unit called lux). Refer the below table to get an overall idea of the type of light values we can expect based on the surrounding environment.
Source — Interpreting lux values
Now, based upon the light value I can turn on/off the device’s torch light or you can do any logic as per your requirement. Lastly, we should stop the sensor from running in the background when not in use, hence invoking our second method stopLightSensor when the component unmounts.
So, that’s about it! We have created our Native Modules in android which uses our device’s light sensor to interpret the light value and we also checked how we can receive that value in our JavaScript end and perform any logic.
I have also published an npm package for the same called react-native-ambient-light-sensor. Check this out and use it if you want in your application!
Also you can checkout the complete GitHub repo link for the package here.
Hope you have enjoyed reading this :)