diff --git a/app/build.gradle b/app/build.gradle index 9d441b48c..c8a7e92bb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,7 +93,6 @@ dependencies { // ExoPlayer def exoplayer_version = "2.14.0" implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version" - implementation "com.google.android.exoplayer:extension-mediasession:$exoplayer_version" // Image loading implementation "io.coil-kt:coil:1.2.1" diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt index 6cd9e956e..ccea6fa25 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt @@ -40,7 +40,7 @@ class PlaybackNotification private constructor( setSmallIcon(R.drawable.ic_song) setCategory(NotificationCompat.CATEGORY_SERVICE) setShowWhen(false) - setNotificationSilent() + setSilent(true) setContentIntent(mainActivityIntent) setVisibility(NotificationCompat.VISIBILITY_PUBLIC) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt index 028f57124..4029812ce 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt @@ -23,7 +23,6 @@ import com.google.android.exoplayer2.RenderersFactory import com.google.android.exoplayer2.SimpleExoPlayer import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer -import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory import com.google.android.exoplayer2.mediacodec.MediaCodecSelector import com.google.android.exoplayer2.source.DefaultMediaSourceFactory @@ -60,6 +59,7 @@ import org.oxycblt.auxio.ui.getSystemServiceSafe class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callback, SettingsManager.Callback { private lateinit var player: SimpleExoPlayer private lateinit var mediaSession: MediaSessionCompat + private lateinit var connector: PlaybackSessionConnector private lateinit var notification: PlaybackNotification private lateinit var notificationManager: NotificationManager @@ -113,15 +113,10 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac // Set up the media button callbacks mediaSession = MediaSessionCompat(this, packageName).apply { isActive = true - - MediaSessionConnector(this).apply { - setPlayer(player) - setMediaButtonEventHandler { _, _, mediaButtonEvent -> - handleMediaButtonEvent(mediaButtonEvent) - } - } } + connector = PlaybackSessionConnector(this, mediaSession) + // Then the notif/headset callbacks IntentFilter().apply { addAction(PlaybackNotification.ACTION_LOOP) @@ -167,6 +162,7 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac unregisterReceiver(systemReceiver) player.release() + connector.release() mediaSession.release() audioReactor.release() releaseWakelock() @@ -305,6 +301,8 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac override fun onShowCoverUpdate(showCovers: Boolean) { playbackManager.song?.let { song -> + connector.onSongUpdate(song) + notification.setMetadata( this, song, settingsManager.colorizeNotif, ::startForegroundOrNotify ) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt new file mode 100644 index 000000000..3be251e6c --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt @@ -0,0 +1,96 @@ +package org.oxycblt.auxio.playback.system + +import android.content.Context +import android.os.SystemClock +import android.support.v4.media.MediaMetadataCompat +import android.support.v4.media.session.MediaSessionCompat +import android.support.v4.media.session.PlaybackStateCompat +import org.oxycblt.auxio.coil.loadBitmap +import org.oxycblt.auxio.music.Song +import org.oxycblt.auxio.playback.state.PlaybackStateManager + +class PlaybackSessionConnector( + private val context: Context, + private val mediaSession: MediaSessionCompat +) : PlaybackStateManager.Callback { + private val playbackManager = PlaybackStateManager.getInstance() + + private val emptyMetadata = MediaMetadataCompat.Builder().build() + private val state = PlaybackStateCompat.Builder() + .setActions(ACTIONS) + + private var playerState = PlaybackStateCompat.STATE_NONE + private var playerPosition = playbackManager.position + + init { + playbackManager.addCallback(this) + + onSongUpdate(playbackManager.song) + onPlayingUpdate(playbackManager.isPlaying) + onPositionUpdate(playbackManager.position) + } + + fun release() { + playbackManager.removeCallback(this) + } + + override fun onSongUpdate(song: Song?) { + if (song == null) { + mediaSession.setMetadata(emptyMetadata) + setPlayerState(PlaybackStateCompat.STATE_STOPPED) + return + } + + val builder = MediaMetadataCompat.Builder() + .putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.name) + .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, song.name) + .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.album.artist.name) + .putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, song.album.artist.name) + .putString(MediaMetadataCompat.METADATA_KEY_COMPOSER, song.album.artist.name) + .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, song.album.artist.name) + .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.album.name) + .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.duration) + + loadBitmap(context, song) { bitmap -> + builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap) + mediaSession.setMetadata(builder.build()) + } + } + + override fun onPlayingUpdate(isPlaying: Boolean) { + setPlayerState( + if (playbackManager.isPlaying) { + PlaybackStateCompat.STATE_PLAYING + } else { + PlaybackStateCompat.STATE_PAUSED + } + ) + } + + override fun onPositionUpdate(position: Long) { + playerPosition = position + updateState() + } + + private fun setPlayerState(state: Int) { + playerState = state + updateState() + } + + private fun updateState() { + state.setState(playerState, playerPosition, 1.0f, SystemClock.elapsedRealtime()) + mediaSession.setPlaybackState(state.build()) + } + + companion object { + const val ACTIONS = PlaybackStateCompat.ACTION_PLAY or + PlaybackStateCompat.ACTION_PAUSE or + PlaybackStateCompat.ACTION_PLAY_PAUSE or + PlaybackStateCompat.ACTION_SET_REPEAT_MODE or + PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE or + PlaybackStateCompat.ACTION_SKIP_TO_NEXT or + PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or + PlaybackStateCompat.ACTION_SEEK_TO or + PlaybackStateCompat.ACTION_STOP + } +}