140 lines
4.8 KiB
Kotlin
140 lines
4.8 KiB
Kotlin
package com.github.apognu.otter.playback
|
|
|
|
import android.app.Notification
|
|
import android.app.PendingIntent
|
|
import android.app.Service
|
|
import android.content.BroadcastReceiver
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.media.MediaMetadata
|
|
import android.support.v4.media.MediaMetadataCompat
|
|
import android.support.v4.media.session.MediaSessionCompat
|
|
import androidx.core.app.NotificationCompat
|
|
import androidx.core.app.NotificationManagerCompat
|
|
import androidx.media.app.NotificationCompat.MediaStyle
|
|
import com.github.apognu.otter.R
|
|
import com.github.apognu.otter.activities.MainActivity
|
|
import com.github.apognu.otter.utils.*
|
|
import com.squareup.picasso.Picasso
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.Dispatchers.Default
|
|
import kotlinx.coroutines.launch
|
|
|
|
class MediaControlsManager(val context: Service, private val scope: CoroutineScope, private val mediaSession: MediaSessionCompat) {
|
|
companion object {
|
|
const val NOTIFICATION_ACTION_OPEN_QUEUE = 0
|
|
const val NOTIFICATION_ACTION_PREVIOUS = 1
|
|
const val NOTIFICATION_ACTION_TOGGLE = 2
|
|
const val NOTIFICATION_ACTION_NEXT = 3
|
|
}
|
|
|
|
private var notification: Notification? = null
|
|
|
|
fun updateNotification(track: Track?, playing: Boolean) {
|
|
if (notification == null && !playing) return
|
|
|
|
track?.let {
|
|
val stateIcon = when (playing) {
|
|
true -> R.drawable.pause
|
|
false -> R.drawable.play
|
|
}
|
|
|
|
scope.launch(Default) {
|
|
val openIntent = Intent(context, MainActivity::class.java).apply { action = NOTIFICATION_ACTION_OPEN_QUEUE.toString() }
|
|
val openPendingIntent = PendingIntent.getActivity(context, 0, openIntent, 0)
|
|
|
|
val coverUrl = maybeNormalizeUrl(track.album.cover.original)
|
|
val cover = coverUrl?.run { Picasso.get().load(coverUrl) }
|
|
|
|
mediaSession.setMetadata(MediaMetadataCompat.Builder().apply {
|
|
putString(MediaMetadata.METADATA_KEY_ARTIST, track.artist.name)
|
|
putString(MediaMetadata.METADATA_KEY_TITLE, track.title)
|
|
putLong(MediaMetadata.METADATA_KEY_DURATION, (track.bestUpload()?.duration?.toLong() ?: 0L) * 1000)
|
|
|
|
cover?.let {
|
|
try { putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, it.get()) } catch (_: Exception) {}
|
|
}
|
|
}.build())
|
|
|
|
notification = NotificationCompat.Builder(
|
|
context,
|
|
AppContext.NOTIFICATION_CHANNEL_MEDIA_CONTROL
|
|
)
|
|
.setShowWhen(false)
|
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
|
.setStyle(
|
|
MediaStyle()
|
|
.setMediaSession(mediaSession.sessionToken)
|
|
.setShowActionsInCompactView(0, 1, 2)
|
|
)
|
|
.setSmallIcon(R.drawable.ottershape)
|
|
.run {
|
|
if (cover != null) {
|
|
try { setLargeIcon(cover.get()) } catch (_: Exception) {}
|
|
|
|
this
|
|
} else this
|
|
}
|
|
.setContentTitle(track.title)
|
|
.setContentText(track.artist.name)
|
|
.setContentIntent(openPendingIntent)
|
|
.setChannelId(AppContext.NOTIFICATION_CHANNEL_MEDIA_CONTROL)
|
|
.addAction(
|
|
action(
|
|
R.drawable.previous, context.getString(R.string.control_previous),
|
|
NOTIFICATION_ACTION_PREVIOUS
|
|
)
|
|
)
|
|
.addAction(
|
|
action(
|
|
stateIcon, context.getString(R.string.control_toggle),
|
|
NOTIFICATION_ACTION_TOGGLE
|
|
)
|
|
)
|
|
.addAction(
|
|
action(
|
|
R.drawable.next, context.getString(R.string.control_next),
|
|
NOTIFICATION_ACTION_NEXT
|
|
)
|
|
)
|
|
.build()
|
|
|
|
notification?.let {
|
|
NotificationManagerCompat.from(context).notify(AppContext.NOTIFICATION_MEDIA_CONTROL, it)
|
|
}
|
|
|
|
if (playing) tick()
|
|
}
|
|
}
|
|
}
|
|
|
|
fun tick() {
|
|
notification?.let {
|
|
context.startForeground(AppContext.NOTIFICATION_MEDIA_CONTROL, it)
|
|
}
|
|
}
|
|
|
|
private fun action(icon: Int, title: String, id: Int): NotificationCompat.Action {
|
|
val intent = Intent(context, MediaControlActionReceiver::class.java).apply { action = id.toString() }
|
|
val pendingIntent = PendingIntent.getBroadcast(context, id, intent, 0)
|
|
|
|
return NotificationCompat.Action.Builder(icon, title, pendingIntent).build()
|
|
}
|
|
}
|
|
|
|
class MediaControlActionReceiver : BroadcastReceiver() {
|
|
override fun onReceive(context: Context?, intent: Intent?) {
|
|
when (intent?.action) {
|
|
MediaControlsManager.NOTIFICATION_ACTION_PREVIOUS.toString() -> CommandBus.send(
|
|
Command.PreviousTrack
|
|
)
|
|
MediaControlsManager.NOTIFICATION_ACTION_TOGGLE.toString() -> CommandBus.send(
|
|
Command.ToggleState
|
|
)
|
|
MediaControlsManager.NOTIFICATION_ACTION_NEXT.toString() -> CommandBus.send(
|
|
Command.NextTrack
|
|
)
|
|
}
|
|
}
|
|
}
|