PiP in React Native for Android

Devpenzil
4 min readApr 25, 2023

--

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

--

--