From 5126ad63660da1b1ac536e4e6503e2d623a8ce67 Mon Sep 17 00:00:00 2001 From: cottongin Date: Wed, 18 Mar 2026 06:41:24 -0400 Subject: [PATCH] fix: post placeholder foreground notification immediately on play MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit startForegroundService() requires startForeground() within ~5 seconds. The async coroutine (DB queries, old job join) delayed the Media3 notification pipeline past this deadline. Now we: - Create the media session in onCreate() instead of lazily - Post a minimal "Connecting…" notification synchronously in onStartCommand before launching the async play coroutine - Media3 replaces it with the proper media notification once the adapter state updates to Playing Made-with: Cursor --- .../radio247/service/RadioPlaybackService.kt | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/xyz/cottongin/radio247/service/RadioPlaybackService.kt b/app/src/main/java/xyz/cottongin/radio247/service/RadioPlaybackService.kt index 0a40e08..9c52051 100644 --- a/app/src/main/java/xyz/cottongin/radio247/service/RadioPlaybackService.kt +++ b/app/src/main/java/xyz/cottongin/radio247/service/RadioPlaybackService.kt @@ -1,5 +1,7 @@ package xyz.cottongin.radio247.service +import android.app.NotificationChannel +import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent @@ -10,6 +12,8 @@ import android.net.NetworkRequest import android.os.Bundle import android.os.PowerManager import android.util.Log +import androidx.core.app.NotificationCompat +import xyz.cottongin.radio247.R import com.google.common.util.concurrent.SettableFuture import androidx.media3.common.MediaItem import androidx.media3.common.MediaMetadata @@ -55,6 +59,8 @@ class ConnectionFailedException(cause: Throwable) : Exception("Connection failed class RadioPlaybackService : MediaLibraryService() { companion object { private const val TAG = "RadioPlayback" + private const val FOREGROUND_CHANNEL_ID = "radio_playback" + private const val FOREGROUND_NOTIFICATION_ID = 1 const val ACTION_PLAY = "xyz.cottongin.radio247.PLAY" const val ACTION_STOP = "xyz.cottongin.radio247.STOP" const val ACTION_PAUSE = "xyz.cottongin.radio247.PAUSE" @@ -118,6 +124,24 @@ class RadioPlaybackService : MediaLibraryService() { override fun onCreate() { super.onCreate() + ensureForegroundChannel() + ensureMediaSession() + } + + private fun ensureForegroundChannel() { + val channel = NotificationChannel( + FOREGROUND_CHANNEL_ID, "Radio Playback", NotificationManager.IMPORTANCE_LOW + ) + getSystemService(NotificationManager::class.java).createNotificationChannel(channel) + } + + private fun postPlaceholderForeground() { + val notification = NotificationCompat.Builder(this, FOREGROUND_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle("Connecting…") + .setSilent(true) + .build() + startForeground(FOREGROUND_NOTIFICATION_ID, notification) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -126,6 +150,7 @@ class RadioPlaybackService : MediaLibraryService() { ACTION_PLAY -> { val stationId = intent.getLongExtra(EXTRA_STATION_ID, -1L) if (stationId >= 0) { + postPlaceholderForeground() launchPlay(stationId) } else { stopSelf() @@ -194,7 +219,6 @@ class RadioPlaybackService : MediaLibraryService() { } acquireLocks() - ensureMediaSession() playerAdapter?.updateStation(station) playerAdapter?.updatePlaybackState(PlaybackState.Connecting(station))