0

I've been researching awhile about how to keep active a constantly running audio playback in the background (online radio). For last I made a foreground service for it and its works for the most phones, but not on Samsung Android P and above... (as this article show in the "Foreground service limitations" section: https://proandroiddev.com/android-foreground-service-restrictions-d3baa93b2f70) I heard that there is a advanced tool for audio playback called ExoPlayer. Could this lib help me out?

I'v been tried these solutions:

  • ping google.com every 2 sec
  • request battery optimization ignoring
  • set wake lock for mediplayer with: setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK) (still in use)

Starting the service:

viewModel.isMusicControlServiceNeedToStart.observe(this, Observer {
        if (it) {
            val intent = Intent(this, MusicControlForegroundServiceImpl::class.java).apply { action = ACTION_SHOW_MUSIC_CONTROL }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) startForegroundService(intent) else startService(intent)
        } else {
            stopService(Intent(this, MusicControlForegroundServiceImpl::class.java))
        }
    })

The service itself:

class MusicControlForegroundServiceImpl : Service(), KoinComponent {

private val notificationManager: NotificationManager by inject()
private val radioManager: RadioManager by inject()
private val context: Context by inject()
private val preferences: Preferences by inject()

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    if (intent != null && intent.action == ACTION_SHOW_MUSIC_CONTROL) {
        val lastSelectedRadio = preferences.getJSON(Globals.LAST_SELECTED_RADIO_KEY, Radio::class.java)
                ?: return START_NOT_STICKY
        val notification = notificationManager.createMediaControlNotificationIfNeeded(context, lastSelectedRadio)
        startForeground(1, notification)
    }
    return START_NOT_STICKY
}

override fun onTaskRemoved(rootIntent: Intent?) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) startForegroundService(rootIntent) else startService(rootIntent)
    super.onTaskRemoved(rootIntent)
}

override fun onDestroy() {
    if (!notificationManager.musicControlServiceRestart) radioManager.release()
    synchronized(MUSIC_CONTROL_SERVICE_LOCK) { notificationManager.musicControlServiceRestart = false }
    synchronized(MEDIA_PLAYER_LOCK) { radioManager.lastPlayedMediaUrl = null }
    stopForeground(true)
    super.onDestroy()
}

override fun onBind(intent: Intent?): IBinder? = null

}

The notification creation:

 override fun createMediaControlNotificationIfNeeded(context: Context, selectedRadio: Radio): Notification {
    val resultIntent = Intent(context, RadioDetailActivity::class.java)
    val resultPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
        addNextIntentWithParentStack(resultIntent)
        getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
    }
    val playIntent = Intent(context, NotificationReceiver::class.java).apply {
        putExtra(MusicState::class.java.name, MusicState.PLAY)
    }
    val pauseIntent = Intent(context, NotificationReceiver::class.java).apply {
        putExtra(MusicState::class.java.name, MusicState.PAUSE)
    }

    val notificationManager = NotificationManagerCompat.from(context)

    @Suppress("DEPRECATION") val builder = NotificationCompat.Builder(context, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_notification_icon)
            .setContentTitle(selectedRadio.name)
            .setDefaults(0)
            .setOngoing(true)
            .setNotificationSilent()
            .addAction(
                    R.drawable.ic_notification_pause,
                    context.getString(R.string.pause),
                    PendingIntent.getBroadcast(context, 1, pauseIntent, 0)
            )
            .addAction(
                    R.drawable.ic_notification_play,
                    context.getString(R.string.play),
                    PendingIntent.getBroadcast(context, 2, playIntent, 0)
            )
            .setStyle(
                    androidx.media.app.NotificationCompat.MediaStyle().setMediaSession(
                            MediaSessionCompat(
                                    context,
                                    RadioDetailActivity::class.java.name
                            ).sessionToken
                    )
            )
            .setContentIntent(resultPendingIntent)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(
                CHANNEL_ID,
                context.getString(R.string.app_name),
                NotificationManager.IMPORTANCE_LOW
        )
        notificationManager.createNotificationChannel(channel)
        builder.setChannelId(CHANNEL_ID)
    }

    return builder.build()
}

If you need any other resources please let me know and help if you can! I'm struggling with this problem for weeks now.. :(

UPDATE Now I throw my media control notification in every 2 minutes to update previous, so the app can survive like 30 minutes on the affected phone, but still not a working solution...

ZombieD
  • 21
  • 1
  • 5
  • there are some device manufacturers that break doze mode and will just kill any and everything like Samsung. The only think you can do is tell the user that they have to whitelist the app – tyczj Nov 04 '20 at 17:00
  • I found an opensource project called RadioDroid what work as same as mine, streaming online radio in background. I tried it on an affected Samsung device and its works somehow... However I couldn't find the solution in it yet... – ZombieD Nov 05 '20 at 13:48

0 Answers0