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
)}