Nowadays, React Native is becoming increasingly popular in the world of mobile development. Many enterprise-level applications are migrating to React Native due to the numerous changes introduced in a short period of time. We need to experiment with new methods and practices in the world of React Native development.
This blog discusses how to create a video player and implement PiP (picture in picture) functionality in React Native for Android.
Create a React Native Application
npx react-native init AwesomeProject
This command creates a bare React Native project in the AwesomeProject
folder.
Install dependencies
For the video player we need a package called react-native-video
npm i react-native-video
To create the Picture-in-Picture (PiP) functionality, we are not using any npm packages. Instead, we are writing a native module in Java. However, before doing so, we need to create the video player.
Creating Video Player
In the app.tsx
write some code for video player.
import {SafeAreaView} from 'react-native';
import React, {useEffect} from 'react';
import Video from 'react-native-video';
import {style} from './Home.style';
const Home = () => {
return (
<SafeAreaView>
<Video
source={{
uri: 'https://player.vimeo.com/external/538571502.sd.mp4?s=931e8b30977433ba260a9e08678ac56e13b73aa0&profile_id=165&oauth2_token_id=57447761',
}}
style={style.videoBox}
pictureInPicture={true}
fullscreen
posterResizeMode="cover"
resizeMode="cover"
repeat={true}
playInBackground
poster="https://images.pexels.com/photos/1546035/pexels-photo-1546035.jpeg?auto=compress&cs=tinysrgb&w=1600"
/>
</SafeAreaView>
);
};
export default Home;
Its all done about the video player, now the video player is working.
Native Module for PiP
If you have no idea how to write a native module in React Native, you can refer to the official documentation: https://reactnative.dev/docs/native-modules-android.
Now, we have to create a file named pipModule.java
in android/app/src/main/java/com/rnchallenge/pipModule.java
and add some code.
package com.package;
import android.app.PictureInPictureParams;
import android.os.Build;
import android.util.Rational;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class PipModule extends ReactContextBaseJavaModule {
private Rational aspectRatio;
PipModule(ReactApplicationContext context){
super(context);
aspectRatio = new Rational(3,4);
}
@Override
public String getName(){
return "PipModule";
}
@ReactMethod
public void EnterPipMode(){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
PictureInPictureParams params = new PictureInPictureParams.Builder().setAspectRatio(this.aspectRatio).build();
getCurrentActivity().enterPictureInPictureMode(params);
}
}
}
The aspectRatio refers to the measurement of the PiP window, which will be displayed in a 3:4 ratio.
The getName() function returns the module name.
The EnterPipMode() function builds a PiP window with the given aspect ratio when triggered.
Its all done from the module side now we have to include it in the mainapplication.java
for that we have to create a new file named pipPackage.java
in android/app/src/main/java/com/rnchallenge/pipModule.java
.
package com.package; // replace your-app-name with your app’s name
import com.facebook.react.ReactPackage;
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 PipPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new PipModule(reactContext));
return modules;
}
}
Then go to mainApplication.java
and add this module in packages list
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 ReactVideoPackage());
packages.add(new PipPackage()); <---- add here
return packages;
}
Export this method to Javascript
Now that we have completed the native side, we need to write a condition to trigger the Picture-in-Picture (PiP) functionality. This means that if the app is running in the background, the PiP window should be displayed. If the app is in the foreground, the PiP should be removed.
To achieve this, we use AppState
, which is provided by React Native. It returns the app's current state (either active
or background
).
To import the PiPModule
from the NativeModule
, follow this syntax:
const {PipModule} = NativeModules;
After all this, write a condition to trigger the PiPModule
useEffect(() => {
const appstatus = AppState.addEventListener('change', e => {
if (e === 'background') {
PipModule.EnterPipMode();
}
});
return () => {
appstatus.remove();
};
}, []);
The final file looks like this
import {SafeAreaView, NativeModules, AppState} from 'react-native';
import React, {useEffect} from 'react';
import Video from 'react-native-video';
import {style} from './Home.style';
const Home = () => {
const {PipModule} = NativeModules;
useEffect(() => {
const appstatus = AppState.addEventListener('change', e => {
if (e === 'background') {
PipModule.EnterPipMode();
}
});
return () => {
appstatus.remove();
};
}, []);
return (
<SafeAreaView>
<Video
source={{
uri: 'https://player.vimeo.com/external/538571502.sd.mp4?s=931e8b30977433ba260a9e08678ac56e13b73aa0&profile_id=165&oauth2_token_id=57447761',
}}
style={style.videoBox}
pictureInPicture={true}
fullscreen
posterResizeMode="cover"
resizeMode="cover"
repeat={true}
playInBackground
poster="https://images.pexels.com/photos/1546035/pexels-photo-1546035.jpeg?auto=compress&cs=tinysrgb&w=1600"
/>
</SafeAreaView>
);
};
export default Home;
Conclusion
Congratulations! You have successfully implemented PiP functionality in your app. There are many other features and customization options available. Refer to the following documentation for more information:
https://developer.android.com/develop/ui/views/picture-in-picture