More potential fixes for the MediaSession issue. Ugh. Wish there were

reliable repro steps.
This commit is contained in:
casey langen 2019-02-17 13:05:45 -08:00
parent 9085ee3020
commit d28a85dc4f
3 changed files with 98 additions and 84 deletions

View File

@ -66,8 +66,8 @@ class SystemService : Service() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel( val channel = NotificationChannel(
NOTIFICATION_CHANNEL, NOTIFICATION_CHANNEL,
NOTIFICATION_CHANNEL, NOTIFICATION_CHANNEL,
NotificationManager.IMPORTANCE_LOW) NotificationManager.IMPORTANCE_LOW)
channel.enableVibration(false) channel.enableVibration(false)
@ -122,7 +122,7 @@ class SystemService : Service() {
if (wakeLock == null) { if (wakeLock == null) {
wakeLock = powerManager.newWakeLock( wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "StreamingPlaybackService:") PowerManager.PARTIAL_WAKE_LOCK, "$SESSION_TAG:")
wakeLock?.let { wakeLock?.let {
it.setReferenceCounted(false) it.setReferenceCounted(false)
@ -159,19 +159,24 @@ class SystemService : Service() {
} }
private fun checkInitMediaSession() { private fun checkInitMediaSession() {
val receiver = ComponentName(packageName, MediaButtonReceiver::class.java.name) if (mediaSession == null || mediaSession?.isActive != true) {
deinitMediaSession()
mediaSession = MediaSessionCompat(this, "musikdroid.SystemService", receiver, null) val receiver = ComponentName(
packageName, MediaButtonReceiver::class.java.name)
mediaSession?.setFlags( mediaSession = MediaSessionCompat(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or this, SESSION_TAG, receiver, null)
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS) .apply {
this.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
this.setCallback(mediaSessionCallback)
this.isActive = true
}
mediaSession?.setCallback(mediaSessionCallback) updateMediaSessionPlaybackState()
}
updateMediaSessionPlaybackState()
mediaSession?.isActive = true
} }
private fun deinitMediaSession() { private fun deinitMediaSession() {
@ -184,14 +189,13 @@ class SystemService : Service() {
registerReceiver(headsetUnpluggedReceiver, filter) registerReceiver(headsetUnpluggedReceiver, filter)
} }
private fun unregisterReceivers() { private fun unregisterReceivers() =
try { try {
unregisterReceiver(headsetUnpluggedReceiver) unregisterReceiver(headsetUnpluggedReceiver)
} }
catch (ex: Exception) { catch (ex: Exception) {
Log.e(TAG, "unable to unregister headset (un)plugged BroadcastReceiver") Log.e(TAG, "unable to unregister headset (un)plugged BroadcastReceiver")
} }
}
private fun updateMediaSessionPlaybackState() { private fun updateMediaSessionPlaybackState() {
var mediaSessionState = PlaybackStateCompat.STATE_STOPPED var mediaSessionState = PlaybackStateCompat.STATE_STOPPED
@ -199,31 +203,33 @@ class SystemService : Service() {
var duration = 0 var duration = 0
var playing: ITrack? = null var playing: ITrack? = null
if (playback != null) { playback?.let {
when (playback?.state) { when (it.state) {
PlaybackState.Playing -> mediaSessionState = PlaybackStateCompat.STATE_PLAYING PlaybackState.Playing -> mediaSessionState = PlaybackStateCompat.STATE_PLAYING
PlaybackState.Buffering -> mediaSessionState = PlaybackStateCompat.STATE_BUFFERING PlaybackState.Buffering -> mediaSessionState = PlaybackStateCompat.STATE_BUFFERING
PlaybackState.Paused -> mediaSessionState = PlaybackStateCompat.STATE_PAUSED PlaybackState.Paused -> mediaSessionState = PlaybackStateCompat.STATE_PAUSED
else -> { } else -> { }
} }
playing = playback!!.playingTrack playing = it.playingTrack
duration = ((playback?.duration ?: 0.0) * 1000).toInt() duration = (it.duration * 1000).toInt()
} }
updateMediaSession(playing, duration) mediaSession?.let {
updateNotification(playing, mediaSessionState) updateMediaSession(playing, duration)
updateNotification(playing, mediaSessionState)
mediaSession?.setPlaybackState(PlaybackStateCompat.Builder() it.setPlaybackState(PlaybackStateCompat.Builder()
.setState(mediaSessionState, 0, 0f) .setState(mediaSessionState, 0, 0f)
.setActions(MEDIA_SESSION_ACTIONS) .setActions(MEDIA_SESSION_ACTIONS)
.build()) .build())
}
} }
private fun downloadAlbumArtIfNecessary(track: ITrack, duration: Int) { private fun downloadAlbumArtIfNecessary(track: ITrack, duration: Int) {
if (!albumArt.same(track) || (albumArt.request == null && albumArt.bitmap == null)) { if (!albumArt.same(track) || (albumArt.request == null && albumArt.bitmap == null)) {
if (track.artist.isNotBlank() && track.album.isNotBlank()) { if (track.artist.isNotBlank() && track.album.isNotBlank()) {
Log.d(TAG, "download") Log.d(TAG, "downloading album art")
val url = getAlbumArtUrl(track, Size.Mega) val url = getAlbumArtUrl(track, Size.Mega)
@ -321,13 +327,11 @@ class SystemService : Service() {
if (state == PlaybackStateCompat.STATE_PAUSED) { if (state == PlaybackStateCompat.STATE_PAUSED) {
notification.addAction(action( notification.addAction(action(
android.R.drawable.ic_media_play, android.R.drawable.ic_media_play,
getString(R.string.button_play), getString(R.string.button_play), ACTION_NOTIFICATION_PLAY))
ACTION_NOTIFICATION_PLAY))
notification.addAction(action( notification.addAction(action(
android.R.drawable.ic_menu_close_clear_cancel, android.R.drawable.ic_menu_close_clear_cancel,
getString(R.string.button_close), getString(R.string.button_close), ACTION_NOTIFICATION_STOP))
ACTION_NOTIFICATION_STOP))
notification.setStyle(MediaStyle() notification.setStyle(MediaStyle()
.setShowActionsInCompactView(0, 1) .setShowActionsInCompactView(0, 1)
@ -336,18 +340,15 @@ class SystemService : Service() {
else { else {
notification.addAction(action( notification.addAction(action(
android.R.drawable.ic_media_previous, android.R.drawable.ic_media_previous,
getString(R.string.button_prev), getString(R.string.button_prev), ACTION_NOTIFICATION_PREV))
ACTION_NOTIFICATION_PREV))
notification.addAction(action( notification.addAction(action(
android.R.drawable.ic_media_pause, android.R.drawable.ic_media_pause,
getString(R.string.button_pause), getString(R.string.button_pause), ACTION_NOTIFICATION_PAUSE))
ACTION_NOTIFICATION_PAUSE))
notification.addAction(action( notification.addAction(action(
android.R.drawable.ic_media_next, android.R.drawable.ic_media_next,
getString(R.string.button_next), getString(R.string.button_next), ACTION_NOTIFICATION_NEXT))
ACTION_NOTIFICATION_NEXT))
notification.setStyle(MediaStyle() notification.setStyle(MediaStyle()
.setShowActionsInCompactView(0, 1, 2) .setShowActionsInCompactView(0, 1, 2)
@ -366,44 +367,49 @@ class SystemService : Service() {
} }
private fun handlePlaybackAction(action: String?): Boolean { private fun handlePlaybackAction(action: String?): Boolean {
if (this.playback != null && Strings.notEmpty(action)) { this.playback?.let {
when (action) { if (Strings.notEmpty(action)) {
ACTION_NOTIFICATION_NEXT -> { when (action) {
this.playback?.next() ACTION_NOTIFICATION_NEXT -> {
return true it.next()
} return true
}
ACTION_NOTIFICATION_PAUSE -> { ACTION_NOTIFICATION_PAUSE -> {
this.playback?.pause() it.pause()
return true return true
} }
ACTION_NOTIFICATION_PLAY -> { ACTION_NOTIFICATION_PLAY -> {
this.playback?.resume() it.resume()
return true return true
} }
ACTION_NOTIFICATION_PREV -> { ACTION_NOTIFICATION_PREV -> {
this.playback?.prev() it.prev()
return true return true
} }
ACTION_NOTIFICATION_STOP -> { ACTION_NOTIFICATION_STOP -> {
this.playback?.stop() it.stop()
shutdown() shutdown()
return true return true
}
} }
} }
} }
return false return false
} }
private val headsetHookDebouncer = object : Debouncer<Void>(HEADSET_HOOK_DEBOUNCE_MS) { private val headsetHookDebouncer = object : Debouncer<Void>(HEADSET_HOOK_DEBOUNCE_MS) {
override fun onDebounced(last: Void?) { override fun onDebounced(last: Void?) {
when (headsetHookPressCount) { playback?.let {
1 -> playback?.pauseOrResume() when (headsetHookPressCount) {
2 -> playback?.next() 1 -> it.pauseOrResume()
3 -> playback?.prev() 2 -> it.next()
3 -> it.prev()
}
} }
headsetHookPressCount = 0 headsetHookPressCount = 0
} }
@ -455,11 +461,11 @@ class SystemService : Service() {
} }
override fun onPlay() { override fun onPlay() {
if (playback?.queueCount == 0) { playback?.let {
playback?.playAll() when (it.queueCount == 0) {
} true -> it.playAll()
else { false -> it.resume()
playback?.resume() }
} }
} }
@ -547,6 +553,7 @@ class SystemService : Service() {
companion object { companion object {
private const val TAG = "SystemService" private const val TAG = "SystemService"
private const val SESSION_TAG = "musikdroid.SystemService"
private const val NOTIFICATION_ID = 0xdeadbeef.toInt() private const val NOTIFICATION_ID = 0xdeadbeef.toInt()
private const val NOTIFICATION_CHANNEL = "musikdroid" private const val NOTIFICATION_CHANNEL = "musikdroid"
private const val HEADSET_HOOK_DEBOUNCE_MS = 500L private const val HEADSET_HOOK_DEBOUNCE_MS = 500L

View File

@ -244,22 +244,29 @@ class MainMetadataView : FrameLayout {
GlideApp.with(context) GlideApp.with(context)
.load(albumArtUrl) .load(albumArtUrl)
.apply(BITMAP_OPTIONS) .apply(BITMAP_OPTIONS)
.listener(object : RequestListener<Drawable> { .listener(object: RequestListener<Drawable> {
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean { override fun onResourceReady(resource: Drawable?,
if (!paused) { model: Any?,
preloadNextImage() target: Target<Drawable>?,
} dataSource: DataSource?,
isFirstResource: Boolean): Boolean {
setMetadataDisplayMode(DisplayMode.Artwork) if (!paused) {
return false preloadNextImage()
} }
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean { setMetadataDisplayMode(DisplayMode.Artwork)
setMetadataDisplayMode(DisplayMode.NoArtwork) return false
loadedAlbumArtUrl = null }
return false
} override fun onLoadFailed(e: GlideException?,
}) model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean): Boolean {
setMetadataDisplayMode(DisplayMode.NoArtwork)
loadedAlbumArtUrl = null
return false
}
})
.into(albumArtImageView) .into(albumArtImageView)
} }
else { else {

View File

@ -99,12 +99,12 @@ private fun dejunk(album: String): String {
object AlbumArtLookup { object AlbumArtLookup {
fun getUrl(album: IAlbum, size: Size = Size.Small): String? { fun getUrl(album: IAlbum, size: Size = Size.Small): String? {
return getThumbnailUrl(album.thumbnailId) return getThumbnailUrl(album.thumbnailId)
?: getUrl(album.albumArtist, album.name, size) ?: getUrl(album.albumArtist, album.name, size)
} }
fun getUrl(track: ITrack, size: Size = Size.Small): String? { fun getUrl(track: ITrack, size: Size = Size.Small): String? {
return getThumbnailUrl(track.thumbnailId) return getThumbnailUrl(track.thumbnailId)
?: getUrl(track.artist, track.album, size) ?: getUrl(track.artist, track.album, size)
} }
fun getUrl(artist: String = "", album: String = "", size: Size = Size.Small): String? { fun getUrl(artist: String = "", album: String = "", size: Size = Size.Small): String? {
@ -121,7 +121,7 @@ object AlbumArtLookup {
fun canIntercept(request: Request): Boolean { fun canIntercept(request: Request): Boolean {
return request.url().host() == "ws.audioscrobbler.com" && return request.url().host() == "ws.audioscrobbler.com" &&
request.url().queryParameter("method") == "album.getinfo" request.url().queryParameter("method") == "album.getinfo"
} }
fun intercept(req: Request): Request? { fun intercept(req: Request): Request? {