Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(android): foreground service types updates #14272

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
package org.jitsi.meet.sdk;


import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
Expand All @@ -40,7 +42,8 @@ public class JitsiMeetMediaProjectionService extends Service {
private static final String TAG = JitsiMeetMediaProjectionService.class.getSimpleName();

public static void launch(Context context) {
OngoingNotification.createOngoingConferenceNotificationChannel();

NotificationUtils.createNotificationChannel();

Intent intent = new Intent(context, JitsiMeetMediaProjectionService.class);

Expand All @@ -55,12 +58,12 @@ public static void launch(Context context) {
} catch (RuntimeException e) {
// Avoid crashing due to ForegroundServiceStartNotAllowedException (API level 31).
// See: https://developer.android.com/guide/components/foreground-services#background-start-restrictions
JitsiMeetLogger.w(TAG + " Ongoing conference service not started", e);
JitsiMeetLogger.w(TAG + "Media projection service not started", e);
return;
}

if (componentName == null) {
JitsiMeetLogger.w(TAG + " Ongoing conference service not started");
JitsiMeetLogger.w(TAG + "Media projection service not started");
}
}

Expand All @@ -73,17 +76,19 @@ public static void abort(Context context) {
public void onCreate() {
super.onCreate();

Notification notification = OngoingNotification.buildOngoingConferenceNotification(null);
Notification notification = MediaProjectionNotification.buildMediaProjectionNotification(this);

if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
JitsiMeetLogger.w(TAG + "Couldn't start service, notification is null");
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(OngoingNotification.NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
startForeground(MediaProjectionNotification.NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
JitsiMeetLogger.w(TAG + "Media projection service started.");
} else {
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
startForeground(MediaProjectionNotification.NOTIFICATION_ID, notification);
JitsiMeetLogger.w(TAG + "Media projection service started.");
}
}

Expand All @@ -95,6 +100,16 @@ public IBinder onBind(Intent intent) {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {

Notification notification = MediaProjectionNotification.buildMediaProjectionNotification(this);

if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + "Couldn't start service, notification is null");
} else {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(MediaProjectionNotification.NOTIFICATION_ID, notification);
}

return START_NOT_STICKY;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class JitsiMeetOngoingConferenceService extends Service
private boolean isAudioMuted;

public static void launch(Context context, HashMap<String, Object> extraData) {
OngoingNotification.createOngoingConferenceNotificationChannel();
NotificationUtils.createNotificationChannel();

Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jitsi.meet.sdk;

import static org.jitsi.meet.sdk.NotificationUtils.ONGOING_CONFERENCE_CHANNEL_ID;

import android.app.Notification;
import android.content.Context;

import androidx.core.app.NotificationCompat;

import org.jitsi.meet.sdk.log.JitsiMeetLogger;

import java.util.Random;

/**
* Helper class for creating the media projection notification which is used with
* {@link JitsiMeetMediaProjectionService}.
*/
class MediaProjectionNotification {
private static final String TAG = MediaProjectionNotification.class.getSimpleName();

static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;

static Notification buildMediaProjectionNotification(Context context) {

if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
return null;
}

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_action))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(false)
.setUsesChronometer(false)
.setAutoCancel(true)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOnlyAlertOnce(true)
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));

return builder.build();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.jitsi.meet.sdk;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;

import org.jitsi.meet.sdk.log.JitsiMeetLogger;

import java.util.ArrayList;
import java.util.List;

class NotificationUtils {
static final String ONGOING_CONFERENCE_CHANNEL_ID = "JitsiOngoingConferenceChannel";

private static final String TAG = NotificationUtils.class.getSimpleName();

public static List<String> allIds = new ArrayList<String>() {{
add(ONGOING_CONFERENCE_CHANNEL_ID);
}};

static void createNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}

Context context = ReactInstanceManagerHolder.getCurrentActivity();
if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
return;
}

NotificationManager notificationManager
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

NotificationChannel channel
= notificationManager.getNotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID);
if (channel != null) {
// The channel was already created, no need to do it again.
return;
}

channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(false);
channel.enableVibration(false);
channel.setShowBadge(false);

notificationManager.createNotificationChannel(channel);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,12 @@

package org.jitsi.meet.sdk;

import static org.jitsi.meet.sdk.NotificationChannels.ONGOING_CONFERENCE_CHANNEL_ID;
import static org.jitsi.meet.sdk.NotificationUtils.ONGOING_CONFERENCE_CHANNEL_ID;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat;
Expand All @@ -42,36 +39,8 @@ class OngoingNotification {
private static final String TAG = OngoingNotification.class.getSimpleName();

static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
private static long startingTime = 0;

static void createOngoingConferenceNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}

Context context = ReactInstanceManagerHolder.getCurrentActivity();
if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
return;
}

NotificationManager notificationManager
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

NotificationChannel channel
= notificationManager.getNotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID);
if (channel != null) {
// The channel was already created, no need to do it again.
return;
}

channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_action_unmute), NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(false);
channel.enableVibration(false);
channel.setShowBadge(false);

notificationManager.createNotificationChannel(channel);
}
private static long startingTime = 0;

static Notification buildOngoingConferenceNotification(Boolean isMuted) {
Context context = ReactInstanceManagerHolder.getCurrentActivity();
Expand All @@ -92,7 +61,7 @@ static Notification buildOngoingConferenceNotification(Boolean isMuted) {
builder
.setCategory(NotificationCompat.CATEGORY_CALL)
.setContentTitle(context.getString(R.string.ongoing_notification_title))
.setContentText(isMuted != null ? context.getString(R.string.ongoing_notification_text) : context.getString(R.string.ongoing_notification_action_screenshare))
.setContentText(context.getString(R.string.ongoing_notification_text))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setOngoing(true)
Expand All @@ -103,10 +72,6 @@ static Notification buildOngoingConferenceNotification(Boolean isMuted) {
.setOnlyAlertOnce(true)
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));

if (isMuted == null) {
return builder.build();
}

NotificationCompat.Action hangupAction = createAction(context, JitsiMeetOngoingConferenceService.Action.HANGUP, R.string.ongoing_notification_action_hang_up);

JitsiMeetOngoingConferenceService.Action toggleAudioAction = isMuted
Expand Down
4 changes: 3 additions & 1 deletion android/sdk/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<resources>
<string name="app_name">Jitsi Meet SDK</string>
<string name="dropbox_app_key"></string>
<string name="media_projection_notification_action">You are currently screen-sharing.</string>
<string name="media_projection_notification_channel_name">Media Projection Notifications</string>
<string name="media_projection_notification_title">Media Projection</string>
<string name="ongoing_notification_title">Ongoing meeting</string>
<string name="ongoing_notification_text">You are currently in a meeting. Tap to return to it.</string>
<string name="ongoing_notification_action_hang_up">Hang up</string>
<string name="ongoing_notification_action_mute">Mute</string>
<string name="ongoing_notification_action_screenshare">You are currently screen-sharing. Tap to return to the meeting.</string>
<string name="ongoing_notification_action_unmute">Unmute</string>
<string name="ongoing_notification_channel_name">Ongoing Conference Notifications</string>
</resources>
8 changes: 3 additions & 5 deletions react/features/base/tracks/actions.native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,12 @@

if (!isSharing) {
_startScreenSharing(dispatch, state);
Platform.OS === 'android' && JitsiMeetMediaProjectionModule.launch();
}

Platform.OS === 'android' && JitsiMeetMediaProjectionModule.abort();
} else {
dispatch(setScreenshareMuted(true));
dispatch(setVideoMuted(false, VIDEO_MUTISM_AUTHORITY.SCREEN_SHARE));
setPictureInPictureEnabled(true);
Platform.OS === 'android' && JitsiMeetMediaProjectionModule.abort();
}
};
}
Expand All @@ -67,9 +65,9 @@
// The first time the user shares the screen we add the track and create the transceiver.
// Afterwards, we just replace the old track, so the transceiver will be reused.
if (currentJitsiTrack) {
dispatch(replaceLocalTrack(currentJitsiTrack, track));
await dispatch(replaceLocalTrack(currentJitsiTrack, track));
} else {
dispatch(addLocalTrack(track));
dispatch(addLocalTrack(track)).then(() => Platform.OS === 'android' && JitsiMeetMediaProjectionModule.launch());

Check failure on line 70 in react/features/base/tracks/actions.native.ts

View workflow job for this annotation

GitHub Actions / Lint

This line has a length of 124. Maximum allowed is 120
}

dispatch(setVideoMuted(true, VIDEO_MUTISM_AUTHORITY.SCREEN_SHARE));
Expand Down
Loading