From b4f69cd9bcf56528a7655a36ed96e113e381d70c Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Wed, 11 Jun 2025 16:18:06 +0200 Subject: [PATCH 1/3] fix: android screenahring issues on new arch --- .../oney/WebRTCModule/GetUserMediaImpl.java | 20 ++++++++++-- .../MediaProjectionNotification.java | 9 +++--- .../WebRTCModule/MediaProjectionService.java | 31 +++++++++++++------ 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java b/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java index be089ea8c..3a047d39e 100644 --- a/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java +++ b/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java @@ -1,9 +1,12 @@ package com.oney.WebRTCModule; import android.app.Activity; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.media.projection.MediaProjectionManager; +import android.os.IBinder; import android.util.DisplayMetrics; import android.util.Log; import androidx.core.util.Consumer; @@ -59,6 +62,20 @@ class GetUserMediaImpl { private Promise displayMediaPromise; private Intent mediaProjectionPermissionResultData; + private final ServiceConnection mediaProjectionServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + // Service is now bound, you can call createScreenStream() + Log.d(TAG, "MediaProjectionService bound, creating screen stream."); + createScreenStream(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + Log.d(TAG, "MediaProjectionService disconnected."); + } + }; + GetUserMediaImpl(WebRTCModule webRTCModule, ReactApplicationContext reactContext) { this.webRTCModule = webRTCModule; this.reactContext = reactContext; @@ -77,8 +94,7 @@ public void onActivityResult(Activity activity, int requestCode, int resultCode, mediaProjectionPermissionResultData = data; ThreadUtils.runOnExecutor(() -> { - MediaProjectionService.launch(activity); - createScreenStream(); + MediaProjectionService.launch(activity, mediaProjectionServiceConnection); }); } } diff --git a/android/src/main/java/com/oney/WebRTCModule/MediaProjectionNotification.java b/android/src/main/java/com/oney/WebRTCModule/MediaProjectionNotification.java index dfe0c879f..be6d05c3d 100644 --- a/android/src/main/java/com/oney/WebRTCModule/MediaProjectionNotification.java +++ b/android/src/main/java/com/oney/WebRTCModule/MediaProjectionNotification.java @@ -61,13 +61,12 @@ static Notification buildMediaProjectionNotification(Context context) { NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ONGOING_CONFERENCE_CHANNEL_ID); builder - .setCategory(NotificationCompat.CATEGORY_CALL) .setContentTitle(context.getString(R.string.media_projection_notification_title)) .setContentText(context.getString(R.string.media_projection_notification_text)) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setOngoing(false) - .setUsesChronometer(false) - .setAutoCancel(true) + .setOngoing(true) + .setSilent(true) + .setWhen(System.currentTimeMillis()) + .setAutoCancel(false) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setOnlyAlertOnce(true) .setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName())) diff --git a/android/src/main/java/com/oney/WebRTCModule/MediaProjectionService.java b/android/src/main/java/com/oney/WebRTCModule/MediaProjectionService.java index c3cd9fe9b..7a0e817a4 100644 --- a/android/src/main/java/com/oney/WebRTCModule/MediaProjectionService.java +++ b/android/src/main/java/com/oney/WebRTCModule/MediaProjectionService.java @@ -1,11 +1,14 @@ package com.oney.WebRTCModule; +import android.app.Activity; import android.app.Notification; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.content.pm.ServiceInfo; +import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.util.Log; @@ -23,22 +26,30 @@ public class MediaProjectionService extends Service { private static final String TAG = MediaProjectionService.class.getSimpleName(); - static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000; + private final IBinder binder = new LocalBinder(); - public static void launch(Context context) { + public class LocalBinder extends Binder { + public MediaProjectionService getService() { + // Return this instance of MediaProjectionService so clients can call public methods + return MediaProjectionService.this; + } + } + + public static void launch(Activity activity, ServiceConnection serviceConnection) { if (!WebRTCModuleOptions.getInstance().enableMediaProjectionService) { return; } - MediaProjectionNotification.createNotificationChannel(context); - Intent intent = new Intent(context, MediaProjectionService.class); + MediaProjectionNotification.createNotificationChannel(activity); + Intent intent = new Intent(activity, MediaProjectionService.class); + activity.bindService(intent, serviceConnection, Context.BIND_IMPORTANT); ComponentName componentName; try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - componentName = context.startForegroundService(intent); + componentName = activity.startForegroundService(intent); } else { - componentName = context.startService(intent); + componentName = activity.startService(intent); } } catch (RuntimeException e) { // Avoid crashing due to ForegroundServiceStartNotAllowedException (API level 31). @@ -65,7 +76,7 @@ public static void abort(Context context) { @Override public IBinder onBind(Intent intent) { - return null; + return binder; } @Override @@ -73,10 +84,12 @@ public int onStartCommand(Intent intent, int flags, int startId) { Notification notification = MediaProjectionNotification.buildMediaProjectionNotification(this); + final int id = (int) (System.currentTimeMillis() % Integer.MAX_VALUE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION); + startForeground(id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION); } else { - startForeground(NOTIFICATION_ID, notification); + startForeground(id, notification); } return START_NOT_STICKY; From 4ce152944e993a7cb12a8db4aaf51c8f9f14cd83 Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Wed, 11 Jun 2025 16:34:24 +0200 Subject: [PATCH 2/3] use java random --- .../java/com/oney/WebRTCModule/MediaProjectionService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/oney/WebRTCModule/MediaProjectionService.java b/android/src/main/java/com/oney/WebRTCModule/MediaProjectionService.java index 7a0e817a4..100a08625 100644 --- a/android/src/main/java/com/oney/WebRTCModule/MediaProjectionService.java +++ b/android/src/main/java/com/oney/WebRTCModule/MediaProjectionService.java @@ -84,7 +84,8 @@ public int onStartCommand(Intent intent, int flags, int startId) { Notification notification = MediaProjectionNotification.buildMediaProjectionNotification(this); - final int id = (int) (System.currentTimeMillis() % Integer.MAX_VALUE); + final Random random = new Random(); + final int id = random.nextInt(Integer.MAX_VALUE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { startForeground(id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION); From c946be82972b32466d16a3499c8415942041d883 Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Wed, 11 Jun 2025 16:42:36 +0200 Subject: [PATCH 3/3] move thread executor only for stream creation --- .../java/com/oney/WebRTCModule/GetUserMediaImpl.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java b/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java index 3a047d39e..abc41042f 100644 --- a/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java +++ b/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java @@ -67,7 +67,9 @@ class GetUserMediaImpl { public void onServiceConnected(ComponentName name, IBinder service) { // Service is now bound, you can call createScreenStream() Log.d(TAG, "MediaProjectionService bound, creating screen stream."); - createScreenStream(); + ThreadUtils.runOnExecutor(() -> { + createScreenStream(); + }); } @Override @@ -93,9 +95,8 @@ public void onActivityResult(Activity activity, int requestCode, int resultCode, mediaProjectionPermissionResultData = data; - ThreadUtils.runOnExecutor(() -> { - MediaProjectionService.launch(activity, mediaProjectionServiceConnection); - }); + MediaProjectionService.launch(activity, mediaProjectionServiceConnection); + } } });