From e5bc2ec353b963bc8c02a4773c452fe7fe0131fe Mon Sep 17 00:00:00 2001 From: Izan Gil <66965250+SrIzan10@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:54:08 +0100 Subject: [PATCH] fix(native): enable Android media projection foreground service --- .../android/app/src/main/AndroidManifest.xml | 33 ++++++++++ .../dev/srizan/helium/viewer/MainActivity.kt | 66 +++++++++++++++++++ native-app/src/hooks/useHeliumStreamer.ts | 7 ++ native-app/src/screens/StreamerScreen.tsx | 8 ++- 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 native-app/android/app/src/main/AndroidManifest.xml create mode 100644 native-app/android/app/src/main/java/dev/srizan/helium/viewer/MainActivity.kt diff --git a/native-app/android/app/src/main/AndroidManifest.xml b/native-app/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..90568b7 --- /dev/null +++ b/native-app/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/native-app/android/app/src/main/java/dev/srizan/helium/viewer/MainActivity.kt b/native-app/android/app/src/main/java/dev/srizan/helium/viewer/MainActivity.kt new file mode 100644 index 0000000..59b18b8 --- /dev/null +++ b/native-app/android/app/src/main/java/dev/srizan/helium/viewer/MainActivity.kt @@ -0,0 +1,66 @@ +package dev.srizan.helium.viewer + +import android.os.Build +import android.os.Bundle + +import com.facebook.react.ReactActivity +import com.facebook.react.ReactActivityDelegate +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled +import com.facebook.react.defaults.DefaultReactActivityDelegate +import com.oney.WebRTCModule.WebRTCModuleOptions + +import expo.modules.ReactActivityDelegateWrapper + +class MainActivity : ReactActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + // Set the theme to AppTheme BEFORE onCreate to support + // coloring the background, status bar, and navigation bar. + // This is required for expo-splash-screen. + setTheme(R.style.AppTheme); + + val webrtcOptions = WebRTCModuleOptions.getInstance() + webrtcOptions.enableMediaProjectionService = true + + super.onCreate(null) + } + + /** + * Returns the name of the main component registered from JavaScript. This is used to schedule + * rendering of the component. + */ + override fun getMainComponentName(): String = "main" + + /** + * 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 { + return ReactActivityDelegateWrapper( + this, + BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, + object : DefaultReactActivityDelegate( + this, + mainComponentName, + fabricEnabled + ){}) + } + + /** + * Align the back button behavior with Android S + * where moving root activities to background instead of finishing activities. + * @see onBackPressed + */ + override fun invokeDefaultOnBackPressed() { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) { + if (!moveTaskToBack(false)) { + // For non-root activities, use the default implementation to finish them. + super.invokeDefaultOnBackPressed() + } + return + } + + // Use the default back button implementation on Android S + // because it's doing more than [Activity.moveTaskToBack] in fact. + super.invokeDefaultOnBackPressed() + } +} diff --git a/native-app/src/hooks/useHeliumStreamer.ts b/native-app/src/hooks/useHeliumStreamer.ts index 3c25f83..a8030dc 100644 --- a/native-app/src/hooks/useHeliumStreamer.ts +++ b/native-app/src/hooks/useHeliumStreamer.ts @@ -239,6 +239,13 @@ export function useHeliumStreamer( const videoTrackCount = stream.getVideoTracks().length; const audioTrackCount = stream.getAudioTracks().length; + + if (!videoTrackCount) { + setStatus("screen capture started without video track"); + stopSharing(); + return; + } + setStatus(`capturing ${videoTrackCount} video / ${audioTrackCount} audio tracks`); stream.getTracks().forEach((track) => { diff --git a/native-app/src/screens/StreamerScreen.tsx b/native-app/src/screens/StreamerScreen.tsx index 9e05a99..8e2cb8d 100644 --- a/native-app/src/screens/StreamerScreen.tsx +++ b/native-app/src/screens/StreamerScreen.tsx @@ -151,7 +151,13 @@ export function StreamerScreen() { {isSharing && streamUrl ? ( - + ) : ( Screen preview appears after sharing starts )}