1
0
Fork 0
mirror of https://github.com/TeamNewPipe/NewPipe.git synced 2025-10-06 03:50:22 +02:00

Post dummy notification then close player service on invalid intent

This should solve "Context.startForegroundService() did not then call Service.startForeground()" according to https://github.com/TeamNewPipe/NewPipe/issues/12489#issuecomment-3290318112
This commit is contained in:
Stypox 2025-09-17 11:45:02 +02:00
parent 92a07a3445
commit aa2b4821e2
No known key found for this signature in database
GPG key ID: 4BDF1B40A49FDD23
2 changed files with 55 additions and 36 deletions

View file

@ -40,6 +40,7 @@ import org.schabi.newpipe.player.mediabrowser.MediaBrowserImpl;
import org.schabi.newpipe.player.mediabrowser.MediaBrowserPlaybackPreparer; import org.schabi.newpipe.player.mediabrowser.MediaBrowserPlaybackPreparer;
import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi; import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi;
import org.schabi.newpipe.player.notification.NotificationPlayerUi; import org.schabi.newpipe.player.notification.NotificationPlayerUi;
import org.schabi.newpipe.player.notification.NotificationUtil;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -156,25 +157,24 @@ public final class PlayerService extends MediaBrowserServiceCompat {
} }
} }
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction()) if (player == null) {
&& (player == null || player.getPlayQueue() == null)) { // No need to process media button's actions or other system intents if the player is
/* // not running. However, since the current intent might have been issued by the system
No need to process media button's actions if the player is not working, otherwise // with `startForegroundService()` (for unknown reasons), we need to ensure that we post
the player service would strangely start with nothing to play // a (dummy) foreground notification, otherwise we'd incur in
Stop the service in this case, which will be removed from the foreground and its // "Context.startForegroundService() did not then call Service.startForeground()". Then
notification cancelled in its destruction // we stop the service again.
*/ Log.d(TAG, "onStartCommand() got a useless intent, closing the service");
NotificationUtil.startForegroundWithDummyNotification(this);
destroyPlayerAndStopService(); destroyPlayerAndStopService();
return START_NOT_STICKY; return START_NOT_STICKY;
} }
if (player != null) {
final PlayerType oldPlayerType = player.getPlayerType(); final PlayerType oldPlayerType = player.getPlayerType();
player.handleIntent(intent); player.handleIntent(intent);
player.handleIntentPost(oldPlayerType); player.handleIntentPost(oldPlayerType);
player.UIs().get(MediaSessionPlayerUi.class) player.UIs().get(MediaSessionPlayerUi.class)
.ifPresent(ui -> ui.handleMediaButtonIntent(intent)); .ifPresent(ui -> ui.handleMediaButtonIntent(intent));
}
return START_NOT_STICKY; return START_NOT_STICKY;
} }

View file

@ -5,7 +5,9 @@ import static androidx.media.app.NotificationCompat.MediaStyle;
import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_CLOSE; import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_CLOSE;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -24,6 +26,7 @@ import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.Player;
import org.schabi.newpipe.player.PlayerIntentType; import org.schabi.newpipe.player.PlayerIntentType;
import org.schabi.newpipe.player.PlayerService;
import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi; import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
@ -90,12 +93,9 @@ public final class NotificationUtil {
Log.d(TAG, "createNotification()"); Log.d(TAG, "createNotification()");
} }
notificationManager = NotificationManagerCompat.from(player.getContext()); notificationManager = NotificationManagerCompat.from(player.getContext());
final NotificationCompat.Builder builder =
new NotificationCompat.Builder(player.getContext(),
player.getContext().getString(R.string.notification_channel_id));
final MediaStyle mediaStyle = new MediaStyle();
// setup media style (compact notification slots and media session) // setup media style (compact notification slots and media session)
final MediaStyle mediaStyle = new MediaStyle();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
// notification actions are ignored on Android 13+, and are replaced by code in // notification actions are ignored on Android 13+, and are replaced by code in
// MediaSessionPlayerUi // MediaSessionPlayerUi
@ -108,18 +108,9 @@ public final class NotificationUtil {
.ifPresent(mediaStyle::setMediaSession); .ifPresent(mediaStyle::setMediaSession);
// setup notification builder // setup notification builder
builder.setStyle(mediaStyle) final var builder = setupNotificationBuilder(player.getContext(), mediaStyle)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
.setShowWhen(false)
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
.setColor(ContextCompat.getColor(player.getContext(),
R.color.dark_background_color))
.setColorized(player.getPrefs().getBoolean( .setColorized(player.getPrefs().getBoolean(
player.getContext().getString(R.string.notification_colorize_key), true)) player.getContext().getString(R.string.notification_colorize_key), true));
.setDeleteIntent(PendingIntentCompat.getBroadcast(player.getContext(),
NOTIFICATION_ID, new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT, false));
// set the initial value for the video thumbnail, updatable with updateNotificationThumbnail // set the initial value for the video thumbnail, updatable with updateNotificationThumbnail
setLargeIcon(builder); setLargeIcon(builder);
@ -168,17 +159,17 @@ public final class NotificationUtil {
&& notificationBuilder.mActions.get(2).actionIntent != null); && notificationBuilder.mActions.get(2).actionIntent != null);
} }
public static void startForegroundWithDummyNotification(final PlayerService service) {
final var builder = setupNotificationBuilder(service, new MediaStyle());
startForeground(service, builder.build());
}
public void createNotificationAndStartForeground() { public void createNotificationAndStartForeground() {
if (notificationBuilder == null) { if (notificationBuilder == null) {
notificationBuilder = createNotification(); notificationBuilder = createNotification();
} }
updateNotification(); updateNotification();
startForeground(player.getService(), notificationBuilder.build());
// ServiceInfo constants are not used below Android Q, so 0 is set here
final int serviceType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
? ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK : 0;
ServiceCompat.startForeground(player.getService(), NOTIFICATION_ID,
notificationBuilder.build(), serviceType);
} }
public void cancelNotificationAndStopForeground() { public void cancelNotificationAndStopForeground() {
@ -192,6 +183,34 @@ public final class NotificationUtil {
} }
/////////////////////////////////////////////////////
// STATIC FUNCTIONS IN COMMON BETWEEN DUMMY AND REAL NOTIFICATION
/////////////////////////////////////////////////////
private static NotificationCompat.Builder setupNotificationBuilder(final Context context,
final MediaStyle style) {
return new NotificationCompat.Builder(context,
context.getString(R.string.notification_channel_id))
.setStyle(style)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
.setShowWhen(false)
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
.setColor(ContextCompat.getColor(context, R.color.dark_background_color))
.setDeleteIntent(PendingIntentCompat.getBroadcast(context,
NOTIFICATION_ID, new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT, false));
}
private static void startForeground(final PlayerService service,
final Notification notification) {
// ServiceInfo constants are not used below Android Q, so 0 is set here
final int serviceType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
? ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK : 0;
ServiceCompat.startForeground(service, NOTIFICATION_ID, notification, serviceType);
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
// ACTIONS // ACTIONS
///////////////////////////////////////////////////// /////////////////////////////////////////////////////