瀏覽代碼

feat(android): fix screen sharing for android 14 (#14419)

* feat(android): media projection is now done through react native webrtc
factor2
Calinteodor 1 年之前
父節點
當前提交
6a3c12b316
沒有連結到貢獻者的電子郵件帳戶。

+ 2
- 2
android/build.gradle 查看文件

@@ -19,9 +19,9 @@ buildscript {
19 19
 ext {
20 20
     kotlinVersion = "1.7.0"
21 21
     buildToolsVersion = "33.0.2"
22
-    compileSdkVersion = 33
22
+    compileSdkVersion = 34
23 23
     minSdkVersion    = 24
24
-    targetSdkVersion = 33
24
+    targetSdkVersion = 34
25 25
     supportLibVersion = "28.0.0"
26 26
 
27 27
     // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.

+ 0
- 5
android/sdk/src/main/AndroidManifest.xml 查看文件

@@ -12,7 +12,6 @@
12 12
     <uses-permission android:name="android.permission.WAKE_LOCK" />
13 13
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
14 14
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
15
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
16 15
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
17 16
 
18 17
     <uses-feature
@@ -51,10 +50,6 @@
51 50
             android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
52 51
             android:foregroundServiceType="mediaPlayback" />
53 52
 
54
-        <service
55
-            android:name="org.jitsi.meet.sdk.JitsiMeetMediaProjectionService"
56
-            android:foregroundServiceType="mediaProjection" />
57
-
58 53
         <provider
59 54
             android:name="com.reactnativecommunity.webview.RNCWebViewFileProvider"
60 55
             android:authorities="${applicationId}.fileprovider"

+ 0
- 44
android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetMediaProjectionModule.java 查看文件

@@ -1,44 +0,0 @@
1
-package org.jitsi.meet.sdk;
2
-
3
-import android.app.Activity;
4
-import android.content.Context;
5
-
6
-import androidx.annotation.NonNull;
7
-
8
-import com.facebook.react.bridge.ReactApplicationContext;
9
-import com.facebook.react.bridge.ReactContextBaseJavaModule;
10
-import com.facebook.react.bridge.ReactMethod;
11
-import com.facebook.react.module.annotations.ReactModule;
12
-
13
-
14
-@ReactModule(name = JitsiMeetMediaProjectionModule.NAME)
15
-class JitsiMeetMediaProjectionModule
16
-    extends ReactContextBaseJavaModule {
17
-
18
-    public static final String NAME = "JitsiMeetMediaProjectionModule";
19
-
20
-    public JitsiMeetMediaProjectionModule(ReactApplicationContext reactContext) {
21
-        super(reactContext);
22
-    }
23
-
24
-    @ReactMethod
25
-    public void launch() {
26
-        Context context = getReactApplicationContext();
27
-        Activity currentActivity = getCurrentActivity();
28
-
29
-        JitsiMeetMediaProjectionService.launch(context, currentActivity);
30
-    }
31
-
32
-    @ReactMethod
33
-    public void abort() {
34
-        Context context = getReactApplicationContext();
35
-
36
-        JitsiMeetMediaProjectionService.abort(context);
37
-    }
38
-
39
-    @NonNull
40
-    @Override
41
-    public String getName() {
42
-        return NAME;
43
-    }
44
-}

+ 0
- 102
android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetMediaProjectionService.java 查看文件

@@ -1,102 +0,0 @@
1
-/*
2
- * Copyright @ 2019-present 8x8, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-package org.jitsi.meet.sdk;
18
-
19
-
20
-import android.app.Activity;
21
-import android.app.Notification;
22
-import android.app.Service;
23
-import android.content.ComponentName;
24
-import android.content.Context;
25
-import android.content.Intent;
26
-import android.content.pm.ServiceInfo;
27
-import android.os.Build;
28
-import android.os.IBinder;
29
-
30
-import org.jitsi.meet.sdk.log.JitsiMeetLogger;
31
-
32
-import java.util.Random;
33
-
34
-/**
35
- * This class implements an Android {@link Service}, a foreground one specifically, and it's
36
- * responsible for presenting an ongoing notification when a conference is in progress.
37
- * The service will help keep the app running while in the background.
38
- *
39
- * See: https://developer.android.com/guide/components/services
40
- */
41
-public class JitsiMeetMediaProjectionService extends Service {
42
-    private static final String TAG = JitsiMeetMediaProjectionService.class.getSimpleName();
43
-
44
-    static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
45
-
46
-    public static void launch(Context context, Activity currentActivity) {
47
-
48
-        NotificationUtils.createNotificationChannel(currentActivity);
49
-
50
-        Intent intent = new Intent(context, JitsiMeetMediaProjectionService.class);
51
-
52
-        ComponentName componentName;
53
-
54
-        try {
55
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
56
-                componentName = context.startForegroundService(intent);
57
-            } else {
58
-                componentName = context.startService(intent);
59
-            }
60
-        } catch (RuntimeException e) {
61
-            // Avoid crashing due to ForegroundServiceStartNotAllowedException (API level 31).
62
-            // See: https://developer.android.com/guide/components/foreground-services#background-start-restrictions
63
-            JitsiMeetLogger.w(TAG + "Media projection service not started", e);
64
-            return;
65
-        }
66
-
67
-        if (componentName == null) {
68
-            JitsiMeetLogger.w(TAG + "Media projection service not started");
69
-        }
70
-    }
71
-
72
-    public static void abort(Context context) {
73
-        Intent intent = new Intent(context, JitsiMeetMediaProjectionService.class);
74
-        context.stopService(intent);
75
-    }
76
-
77
-    @Override
78
-    public IBinder onBind(Intent intent) {
79
-        return null;
80
-    }
81
-
82
-    @Override
83
-    public int onStartCommand(Intent intent, int flags, int startId) {
84
-
85
-        Notification notification = MediaProjectionNotification.buildMediaProjectionNotification(this);
86
-
87
-        if (notification == null) {
88
-            stopSelf();
89
-            JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
90
-
91
-            return START_NOT_STICKY;
92
-        }
93
-
94
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
95
-            startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
96
-        } else {
97
-            startForeground(NOTIFICATION_ID, notification);
98
-        }
99
-
100
-        return START_NOT_STICKY;
101
-    }
102
-}

+ 2
- 2
android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetOngoingConferenceService.java 查看文件

@@ -58,8 +58,8 @@ public class JitsiMeetOngoingConferenceService extends Service
58 58
 
59 59
 
60 60
     public static void launch(Context context, HashMap<String, Object> extraData) {
61
-        
62
-        NotificationUtils.createNotificationChannel((Activity) context);
61
+
62
+        OngoingNotification.createNotificationChannel((Activity) context);
63 63
 
64 64
         Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
65 65
 

+ 0
- 58
android/sdk/src/main/java/org/jitsi/meet/sdk/MediaProjectionNotification.java 查看文件

@@ -1,58 +0,0 @@
1
-/*
2
- * Copyright @ 2019-present 8x8, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-package org.jitsi.meet.sdk;
18
-
19
-import static org.jitsi.meet.sdk.NotificationUtils.ONGOING_CONFERENCE_CHANNEL_ID;
20
-
21
-import android.app.Notification;
22
-import android.content.Context;
23
-
24
-import androidx.core.app.NotificationCompat;
25
-
26
-import org.jitsi.meet.sdk.log.JitsiMeetLogger;
27
-
28
-/**
29
- * Helper class for creating the media projection notification which is used with
30
- * {@link JitsiMeetMediaProjectionService}.
31
- */
32
-class MediaProjectionNotification {
33
-    private static final String TAG = MediaProjectionNotification.class.getSimpleName();
34
-
35
-    static Notification buildMediaProjectionNotification(Context context) {
36
-
37
-        if (context == null) {
38
-            JitsiMeetLogger.d(TAG, " Cannot create notification: no current context");
39
-            return null;
40
-        }
41
-
42
-        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ONGOING_CONFERENCE_CHANNEL_ID);
43
-
44
-        builder
45
-            .setCategory(NotificationCompat.CATEGORY_CALL)
46
-            .setContentTitle(context.getString(R.string.media_projection_notification_title))
47
-            .setContentText(context.getString(R.string.media_projection_notification_text))
48
-            .setPriority(NotificationCompat.PRIORITY_LOW)
49
-            .setOngoing(false)
50
-            .setUsesChronometer(false)
51
-            .setAutoCancel(true)
52
-            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
53
-            .setOnlyAlertOnce(true)
54
-            .setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));
55
-
56
-        return builder.build();
57
-    }
58
-}

+ 0
- 50
android/sdk/src/main/java/org/jitsi/meet/sdk/NotificationUtils.java 查看文件

@@ -1,50 +0,0 @@
1
-package org.jitsi.meet.sdk;
2
-
3
-import android.app.Activity;
4
-import android.app.NotificationChannel;
5
-import android.app.NotificationManager;
6
-import android.content.Context;
7
-import android.os.Build;
8
-
9
-import org.jitsi.meet.sdk.log.JitsiMeetLogger;
10
-
11
-import java.util.ArrayList;
12
-import java.util.List;
13
-
14
-class NotificationUtils {
15
-
16
-    static final String ONGOING_CONFERENCE_CHANNEL_ID = "JitsiOngoingConferenceChannel";
17
-
18
-    public static List<String> allIds = new ArrayList<String>() {{ add(ONGOING_CONFERENCE_CHANNEL_ID); }};
19
-
20
-    private static final String TAG = NotificationUtils.class.getSimpleName();
21
-
22
-    static void createNotificationChannel(Activity context) {
23
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
24
-            return;
25
-        }
26
-
27
-        if (context == null) {
28
-            JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
29
-            return;
30
-        }
31
-
32
-        NotificationManager notificationManager
33
-            = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
34
-
35
-        NotificationChannel channel
36
-            = notificationManager.getNotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID);
37
-
38
-        if (channel != null) {
39
-            // The channel was already created, no need to do it again.
40
-            return;
41
-        }
42
-
43
-        channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT);
44
-        channel.enableLights(false);
45
-        channel.enableVibration(false);
46
-        channel.setShowBadge(false);
47
-
48
-        notificationManager.createNotificationChannel(channel);
49
-    }
50
-}

+ 37
- 2
android/sdk/src/main/java/org/jitsi/meet/sdk/OngoingNotification.java 查看文件

@@ -16,8 +16,11 @@
16 16
 
17 17
 package org.jitsi.meet.sdk;
18 18
 
19
-import static org.jitsi.meet.sdk.NotificationUtils.ONGOING_CONFERENCE_CHANNEL_ID;
19
+import org.jitsi.meet.sdk.log.JitsiMeetLogger;
20 20
 
21
+import android.app.Activity;
22
+import android.app.NotificationChannel;
23
+import android.app.NotificationManager;
21 24
 import android.app.Notification;
22 25
 import android.app.PendingIntent;
23 26
 import android.content.Context;
@@ -26,7 +29,8 @@ import android.content.Intent;
26 29
 import androidx.annotation.StringRes;
27 30
 import androidx.core.app.NotificationCompat;
28 31
 
29
-import org.jitsi.meet.sdk.log.JitsiMeetLogger;
32
+import android.os.Build;
33
+
30 34
 
31 35
 /**
32 36
  * Helper class for creating the ongoing notification which is used with
@@ -38,6 +42,37 @@ class OngoingNotification {
38 42
 
39 43
     private static long startingTime = 0;
40 44
 
45
+    static final String ONGOING_CONFERENCE_CHANNEL_ID = "JitsiOngoingConferenceChannel";
46
+
47
+    static void createNotificationChannel(Activity context) {
48
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
49
+            return;
50
+        }
51
+
52
+        if (context == null) {
53
+            JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
54
+            return;
55
+        }
56
+
57
+        NotificationManager notificationManager
58
+            = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
59
+
60
+        NotificationChannel channel
61
+            = notificationManager.getNotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID);
62
+
63
+        if (channel != null) {
64
+            // The channel was already created, no need to do it again.
65
+            return;
66
+        }
67
+
68
+        channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT);
69
+        channel.enableLights(false);
70
+        channel.enableVibration(false);
71
+        channel.setShowBadge(false);
72
+
73
+        notificationManager.createNotificationChannel(channel);
74
+    }
75
+
41 76
     static Notification buildOngoingConferenceNotification(Boolean isMuted, Context context) {
42 77
 
43 78
         if (context == null) {

+ 3
- 1
android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java 查看文件

@@ -37,6 +37,7 @@ import com.oney.WebRTCModule.webrtcutils.H264AndSoftwareVideoEncoderFactory;
37 37
 
38 38
 import org.devio.rn.splashscreen.SplashScreenModule;
39 39
 import org.webrtc.EglBase;
40
+import org.webrtc.Logging;
40 41
 
41 42
 import java.lang.reflect.Constructor;
42 43
 import java.util.ArrayList;
@@ -67,7 +68,6 @@ class ReactInstanceManagerHolder {
67 68
                 new DropboxModule(reactContext),
68 69
                 new ExternalAPIModule(reactContext),
69 70
                 new JavaScriptSandboxModule(reactContext),
70
-                new JitsiMeetMediaProjectionModule(reactContext),
71 71
                 new LocaleDetector(reactContext),
72 72
                 new LogBridgeModule(reactContext),
73 73
                 new SplashScreenModule(reactContext),
@@ -241,6 +241,8 @@ class ReactInstanceManagerHolder {
241 241
 
242 242
         options.videoDecoderFactory = new H264AndSoftwareVideoDecoderFactory(eglContext);
243 243
         options.videoEncoderFactory = new H264AndSoftwareVideoEncoderFactory(eglContext);
244
+        options.enableMediaProjectionService = true;
245
+//        options.loggingSeverity = Logging.Severity.LS_INFO;
244 246
 
245 247
         Log.d(TAG, "initializing RN with Activity");
246 248
 

+ 1
- 7
react/features/base/tracks/actions.native.ts 查看文件

@@ -1,5 +1,3 @@
1
-import { NativeModules, Platform } from 'react-native';
2
-
3 1
 import { IReduxState, IStore } from '../../app/types';
4 2
 import { setPictureInPictureEnabled } from '../../mobile/picture-in-picture/functions';
5 3
 import { showNotification } from '../../notifications/actions';
@@ -16,7 +14,6 @@ import { VIDEO_MUTISM_AUTHORITY } from '../media/constants';
16 14
 import { addLocalTrack, replaceLocalTrack } from './actions.any';
17 15
 import { getLocalDesktopTrack, getTrackState, isLocalVideoTrackDesktop } from './functions.native';
18 16
 
19
-const { JitsiMeetMediaProjectionModule } = NativeModules;
20 17
 
21 18
 export * from './actions.any';
22 19
 
@@ -35,10 +32,7 @@ export function toggleScreensharing(enabled: boolean, _ignore1?: boolean, _ignor
35 32
         if (enabled) {
36 33
             const isSharing = isLocalVideoTrackDesktop(state);
37 34
 
38
-            if (isSharing) {
39
-                Platform.OS === 'android' && JitsiMeetMediaProjectionModule.abort();
40
-            } else {
41
-                Platform.OS === 'android' && JitsiMeetMediaProjectionModule.launch();
35
+            if (!isSharing) {
42 36
                 _startScreenSharing(dispatch, state);
43 37
             }
44 38
         } else {

Loading…
取消
儲存