playback: fix notif issues on older devices
- Slight coroutine delay in cover fetch causes the notif to flicker - Default play/pause actions look absolutely hideous
This commit is contained in:
parent
b99cd96726
commit
8b7b916489
8 changed files with 116 additions and 24 deletions
|
@ -18,22 +18,31 @@
|
|||
|
||||
package org.oxycblt.auxio.image.service
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.common.util.BitmapLoader
|
||||
import coil.ImageLoader
|
||||
import coil.memory.MemoryCache
|
||||
import coil.request.Options
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import org.oxycblt.auxio.image.BitmapProvider
|
||||
import org.oxycblt.auxio.image.extractor.SongKeyer
|
||||
import org.oxycblt.auxio.music.MusicRepository
|
||||
import org.oxycblt.auxio.music.service.MediaSessionUID
|
||||
|
||||
class MediaSessionBitmapLoader
|
||||
@Inject
|
||||
constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val musicRepository: MusicRepository,
|
||||
private val bitmapProvider: BitmapProvider
|
||||
private val bitmapProvider: BitmapProvider,
|
||||
private val songKeyer: SongKeyer,
|
||||
private val imageLoader: ImageLoader,
|
||||
) : BitmapLoader {
|
||||
override fun decodeBitmap(data: ByteArray): ListenableFuture<Bitmap> {
|
||||
throw NotImplementedError()
|
||||
|
@ -58,6 +67,13 @@ constructor(
|
|||
else -> return null
|
||||
}
|
||||
?: return null
|
||||
// Even launching a coroutine to obtained cached covers is enough to make the notification
|
||||
// go without covers.
|
||||
val key = songKeyer.key(listOf(song), Options(context))
|
||||
if (imageLoader.memoryCache?.get(MemoryCache.Key(key)) != null) {
|
||||
future.set(imageLoader.memoryCache?.get(MemoryCache.Key(key))?.bitmap)
|
||||
return future
|
||||
}
|
||||
bitmapProvider.load(
|
||||
song,
|
||||
object : BitmapProvider.Target {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package org.oxycblt.auxio.music.service
|
||||
|
||||
import android.content.Context
|
||||
|
@ -141,10 +141,8 @@ constructor(
|
|||
is MediaSessionUID.Category -> return uid.toMediaItem(context)
|
||||
is MediaSessionUID.Single ->
|
||||
musicRepository.find(uid.uid)?.let { musicRepository.find(it.uid) }
|
||||
|
||||
is MediaSessionUID.Joined ->
|
||||
musicRepository.find(uid.childUid)?.let { musicRepository.find(it.uid) }
|
||||
|
||||
null -> null
|
||||
}
|
||||
?: return null
|
||||
|
@ -179,40 +177,32 @@ constructor(
|
|||
when (mediaSessionUID) {
|
||||
MediaSessionUID.Category.ROOT ->
|
||||
MediaSessionUID.Category.IMPORTANT.map { it.toMediaItem(context) }
|
||||
|
||||
MediaSessionUID.Category.SONGS ->
|
||||
listSettings.songSort.songs(deviceLibrary.songs).map {
|
||||
it.toMediaItem(context, null)
|
||||
}
|
||||
|
||||
MediaSessionUID.Category.ALBUMS ->
|
||||
listSettings.albumSort.albums(deviceLibrary.albums).map {
|
||||
it.toMediaItem(context)
|
||||
}
|
||||
|
||||
MediaSessionUID.Category.ARTISTS ->
|
||||
listSettings.artistSort.artists(deviceLibrary.artists).map {
|
||||
it.toMediaItem(context)
|
||||
}
|
||||
|
||||
MediaSessionUID.Category.GENRES ->
|
||||
listSettings.genreSort.genres(deviceLibrary.genres).map {
|
||||
it.toMediaItem(context)
|
||||
}
|
||||
|
||||
MediaSessionUID.Category.PLAYLISTS ->
|
||||
userLibrary.playlists.map { it.toMediaItem(context) }
|
||||
}
|
||||
}
|
||||
|
||||
is MediaSessionUID.Single -> {
|
||||
getChildMediaItems(mediaSessionUID.uid)
|
||||
}
|
||||
|
||||
is MediaSessionUID.Joined -> {
|
||||
getChildMediaItems(mediaSessionUID.childUid)
|
||||
}
|
||||
|
||||
null -> {
|
||||
return null
|
||||
}
|
||||
|
@ -225,24 +215,20 @@ constructor(
|
|||
val songs = listSettings.albumSongSort.songs(item.songs)
|
||||
songs.map { it.toMediaItem(context, item) }
|
||||
}
|
||||
|
||||
is Artist -> {
|
||||
val albums = ARTIST_ALBUMS_SORT.albums(item.explicitAlbums + item.implicitAlbums)
|
||||
val songs = listSettings.artistSongSort.songs(item.songs)
|
||||
albums.map { it.toMediaItem(context) } + songs.map { it.toMediaItem(context, item) }
|
||||
}
|
||||
|
||||
is Genre -> {
|
||||
val artists = GENRE_ARTISTS_SORT.artists(item.artists)
|
||||
val songs = listSettings.genreSongSort.songs(item.songs)
|
||||
artists.map { it.toMediaItem(context) } +
|
||||
songs.map { it.toMediaItem(context, null) }
|
||||
songs.map { it.toMediaItem(context, null) }
|
||||
}
|
||||
|
||||
is Playlist -> {
|
||||
item.songs.map { it.toMediaItem(context, item) }
|
||||
}
|
||||
|
||||
is Song,
|
||||
null -> return null
|
||||
}
|
||||
|
@ -339,8 +325,7 @@ constructor(
|
|||
deviceLibrary.albums,
|
||||
deviceLibrary.artists,
|
||||
deviceLibrary.genres,
|
||||
userLibrary.playlists
|
||||
)
|
||||
userLibrary.playlists)
|
||||
val results = searchEngine.search(items, query)
|
||||
for (entry in searchSubscribers.entries) {
|
||||
if (entry.value == query) {
|
||||
|
|
|
@ -106,7 +106,8 @@ class ExoPlaybackStateHolder(
|
|||
private set
|
||||
|
||||
val mediaSessionPlayer: Player
|
||||
get() = MediaSessionPlayer(player, playbackManager, commandFactory, musicRepository)
|
||||
get() =
|
||||
MediaSessionPlayer(context, player, playbackManager, commandFactory, musicRepository)
|
||||
|
||||
override val progression: Progression
|
||||
get() {
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.oxycblt.auxio.playback.service
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.Surface
|
||||
import android.view.SurfaceHolder
|
||||
import android.view.SurfaceView
|
||||
|
@ -31,6 +33,7 @@ import androidx.media3.common.PlaybackParameters
|
|||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.TrackSelectionParameters
|
||||
import java.lang.Exception
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
|
@ -60,6 +63,7 @@ import org.oxycblt.auxio.util.logE
|
|||
* @author Alexander Capehart
|
||||
*/
|
||||
class MediaSessionPlayer(
|
||||
private val context: Context,
|
||||
player: Player,
|
||||
private val playbackManager: PlaybackStateManager,
|
||||
private val commandFactory: PlaybackCommand.Factory,
|
||||
|
@ -86,6 +90,20 @@ class MediaSessionPlayer(
|
|||
setMediaItems(mediaItems, C.INDEX_UNSET, C.TIME_UNSET)
|
||||
}
|
||||
|
||||
override fun getMediaMetadata() =
|
||||
super.getMediaMetadata().run {
|
||||
val existingExtras = extras
|
||||
val newExtras = existingExtras?.let { Bundle(it) } ?: Bundle()
|
||||
newExtras.apply {
|
||||
putString(
|
||||
"parent",
|
||||
playbackManager.parent?.name?.resolve(context)
|
||||
?: context.getString(R.string.lbl_all_songs))
|
||||
}
|
||||
|
||||
buildUpon().setExtras(newExtras).build()
|
||||
}
|
||||
|
||||
override fun setMediaItems(
|
||||
mediaItems: MutableList<MediaItem>,
|
||||
startIndex: Int,
|
||||
|
|
|
@ -255,7 +255,7 @@ constructor(
|
|||
mediaSession.setCustomLayout(layout)
|
||||
}
|
||||
|
||||
override fun invalidate(ids: Map<String, Int>){
|
||||
override fun invalidate(ids: Map<String, Int>) {
|
||||
for (id in ids) {
|
||||
mediaSession.notifyChildrenChanged(id.key, id.value, null)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* SystemPlaybackReciever.kt is part of Auxio.
|
||||
* PlaybackActionHandler.kt is part of Auxio.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -25,7 +25,9 @@ import android.content.IntentFilter
|
|||
import android.media.AudioManager
|
||||
import android.os.Bundle
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.session.CommandButton
|
||||
import androidx.media3.session.DefaultMediaNotificationProvider
|
||||
import androidx.media3.session.SessionCommand
|
||||
import androidx.media3.session.SessionCommands
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
|
@ -36,6 +38,7 @@ import org.oxycblt.auxio.music.Song
|
|||
import org.oxycblt.auxio.playback.ActionMode
|
||||
import org.oxycblt.auxio.playback.PlaybackSettings
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
import org.oxycblt.auxio.playback.state.Progression
|
||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.widgets.WidgetComponent
|
||||
|
@ -102,6 +105,13 @@ constructor(
|
|||
.setDisplayName(context.getString(R.string.desc_change_repeat))
|
||||
.setSessionCommand(
|
||||
SessionCommand(PlaybackActions.ACTION_INC_REPEAT_MODE, Bundle()))
|
||||
.setEnabled(true)
|
||||
.setExtras(
|
||||
Bundle().apply {
|
||||
putInt(
|
||||
DefaultMediaNotificationProvider.COMMAND_KEY_COMPACT_VIEW_INDEX,
|
||||
0)
|
||||
})
|
||||
.build())
|
||||
}
|
||||
ActionMode.SHUFFLE -> {
|
||||
|
@ -113,16 +123,56 @@ constructor(
|
|||
.setDisplayName(context.getString(R.string.lbl_shuffle))
|
||||
.setSessionCommand(
|
||||
SessionCommand(PlaybackActions.ACTION_INVERT_SHUFFLE, Bundle()))
|
||||
.setEnabled(true)
|
||||
.build())
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
actions.add(
|
||||
CommandButton.Builder()
|
||||
.setIconResId(R.drawable.ic_skip_prev_24)
|
||||
.setDisplayName(context.getString(R.string.desc_skip_prev))
|
||||
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||
.setEnabled(true)
|
||||
.setExtras(
|
||||
Bundle().apply {
|
||||
putInt(DefaultMediaNotificationProvider.COMMAND_KEY_COMPACT_VIEW_INDEX, 1)
|
||||
})
|
||||
.build())
|
||||
|
||||
actions.add(
|
||||
CommandButton.Builder()
|
||||
.setIconResId(
|
||||
if (playbackManager.progression.isPlaying) R.drawable.ic_pause_24
|
||||
else R.drawable.ic_play_24)
|
||||
.setDisplayName(context.getString(R.string.desc_play_pause))
|
||||
.setPlayerCommand(Player.COMMAND_PLAY_PAUSE)
|
||||
.setEnabled(true)
|
||||
.setExtras(
|
||||
Bundle().apply {
|
||||
putInt(DefaultMediaNotificationProvider.COMMAND_KEY_COMPACT_VIEW_INDEX, 2)
|
||||
})
|
||||
.build())
|
||||
|
||||
actions.add(
|
||||
CommandButton.Builder()
|
||||
.setIconResId(R.drawable.ic_skip_next_24)
|
||||
.setDisplayName(context.getString(R.string.desc_skip_next))
|
||||
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||
.setEnabled(true)
|
||||
.setExtras(
|
||||
Bundle().apply {
|
||||
putInt(DefaultMediaNotificationProvider.COMMAND_KEY_COMPACT_VIEW_INDEX, 3)
|
||||
})
|
||||
.build())
|
||||
|
||||
actions.add(
|
||||
CommandButton.Builder()
|
||||
.setIconResId(R.drawable.ic_close_24)
|
||||
.setDisplayName(context.getString(R.string.desc_exit))
|
||||
.setSessionCommand(SessionCommand(PlaybackActions.ACTION_EXIT, Bundle()))
|
||||
.setEnabled(true)
|
||||
.build())
|
||||
|
||||
return actions
|
||||
|
@ -133,6 +183,11 @@ constructor(
|
|||
callback?.onCustomLayoutChanged(createCustomLayout())
|
||||
}
|
||||
|
||||
override fun onProgressionChanged(progression: Progression) {
|
||||
super.onProgressionChanged(progression)
|
||||
callback?.onCustomLayoutChanged(createCustomLayout())
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged(repeatMode: RepeatMode) {
|
||||
super.onRepeatModeChanged(repeatMode)
|
||||
callback?.onCustomLayoutChanged(createCustomLayout())
|
|
@ -1,2 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Auxio Project
|
||||
* Tasker.kt is part of Auxio.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.tasker
|
||||
|
||||
|
|
2
media
2
media
|
@ -1 +1 @@
|
|||
Subproject commit bfa4c10f773bb9336d9c7dade490463318b12ab6
|
||||
Subproject commit 6c77cfa13c83bf2ae5188603d2c9a51ec4cb3ac3
|
Loading…
Reference in a new issue