react-native-whip-whep
Version:
An implementation of WHIP and WHEP protocols functionalities for React Native.
134 lines (116 loc) • 3.97 kB
text/typescript
import {
AndroidConfig,
ConfigPlugin,
withAndroidManifest,
} from '@expo/config-plugins';
import { getMainApplicationOrThrow } from '@expo/config-plugins/build/android/Manifest';
import { WhipWhepPluginOptions } from './types';
/**
* Adds the FOREGROUND_SERVICE permission required for screen sharing.
*
* This permission is required on Android 9+ (API 28+) to start a foreground service.
*/
const withWhipWhepForegroundServicePermission: ConfigPlugin<WhipWhepPluginOptions> = (
config,
props,
) =>
withAndroidManifest(config, (configuration) => {
if (!props?.android?.enableScreensharing) {
return configuration;
}
const mainApplication = configuration.modResults;
if (!mainApplication.manifest) {
return configuration;
}
if (!mainApplication.manifest['uses-permission']) {
mainApplication.manifest['uses-permission'] = [];
}
const permissions = mainApplication.manifest['uses-permission'];
const hasForegroundServicePermission = permissions.some(
(perm) => perm.$?.['android:name'] === 'android.permission.FOREGROUND_SERVICE',
);
if (!hasForegroundServicePermission) {
permissions.push({
$: {
'android:name': 'android.permission.FOREGROUND_SERVICE',
},
});
}
return configuration;
});
/**
* Adds foreground service configuration for screen sharing on Android.
*
* Android requires a foreground service with mediaProjection type to capture the screen.
* This service must:
* - Show a persistent notification while screen sharing is active
* - Declare mediaProjection as foregroundServiceType
* - Stop when the app task is removed (stopWithTask: true)
*
* The service will be used by MediaProjection API to capture screen frames.
*/
const withWhipWhepForegroundService: ConfigPlugin<WhipWhepPluginOptions> = (
config,
props,
) =>
withAndroidManifest(config, async (configuration) => {
if (!props?.android?.enableScreensharing) {
return configuration;
}
const mainApplication = getMainApplicationOrThrow(configuration.modResults);
mainApplication.service = mainApplication.service || [];
const newService = {
$: {
'android:name':
'com.swmansion.reactnativeclient.foregroundService.ScreenCaptureService',
'android:foregroundServiceType': 'mediaProjection',
},
};
const existingServiceIndex = mainApplication.service.findIndex(
(service) => service.$['android:name'] === newService.$['android:name'],
);
if (existingServiceIndex !== -1) {
mainApplication.service[existingServiceIndex] = newService;
} else {
mainApplication.service.push(newService);
}
return configuration;
});
/**
* Adds Picture-in-Picture support to the main activity.
*
* When enabled, allows the app to enter PiP mode during video streaming.
*/
const withWhipWhepPictureInPicture: ConfigPlugin<WhipWhepPluginOptions> = (
config,
props,
) =>
withAndroidManifest(config, (configuration) => {
const activity = AndroidConfig.Manifest.getMainActivityOrThrow(
configuration.modResults,
);
if (props?.android?.supportsPictureInPicture) {
activity.$['android:supportsPictureInPicture'] = 'true';
} else {
delete activity.$['android:supportsPictureInPicture'];
}
return configuration;
});
/**
* Main Android plugin function.
* Applies all Android-specific configurations based on options.
*
* This orchestrates:
* - Foreground service permission (if screen sharing enabled)
* - Foreground service setup (if screen sharing enabled)
* - Picture-in-Picture support
*/
export const withWhipWhepAndroid: ConfigPlugin<WhipWhepPluginOptions> = (
config,
props,
) => {
config = withWhipWhepForegroundServicePermission(config, props);
config = withWhipWhepForegroundService(config, props);
config = withWhipWhepPictureInPicture(config, props);
return config;
};