Потратив целую неделю на то, чтобы разобраться в этой невероятно сложной библиотеке, я пришел в замешательство. Я убежден, что существует готовое решение для этого пользовательского интерфейса мультимедийных уведомлений, которое в наши дни есть практически в каждом крупном приложении с воспроизведением мультимедиа. Он имеет кнопки поиска вперед и назад, кнопку скорости воспроизведения и кнопку избранного:
Я прочитал этот выпуск, где разработчик признает, что это сложно (и обещает задокументировать это, лол), но я не понимаю решения, которое он объясняет.
Я не использую PlayerNotificationManager
.
Вот что у меня есть до сих пор, что не работает. В моем PlaybackService
:
private val back = CommandButton.Builder()
.setEnabled(true)
.setIconResId(androidx.media3.session.R.drawable.media3_notification_seek_back)
.setDisplayName("Seek Back")
.setPlayerCommand(Player.COMMAND_SEEK_BACK)
.build()
private val forward = CommandButton.Builder()
.setEnabled(true)
.setIconResId(androidx.media3.session.R.drawable.media3_notification_seek_forward)
.setDisplayName("Seek Forward")
.setPlayerCommand(Player.COMMAND_SEEK_FORWARD)
.build()
private val buttons = ImmutableList.of(back, forward)
override fun onCreate() {
super.onCreate()
val intent = packageManager!!.getLaunchIntentForPackage(packageName)!!
.let { sessionIntent ->
PendingIntent.getActivity(this, SESSION_INTENT_REQUEST_CODE, sessionIntent, PendingIntent.FLAG_IMMUTABLE)
}
_session = MediaSession.Builder(this, buildPlayer())
.setSessionActivity(intent)
.setCustomLayout(buttons)
.setCallback(CustomMediaSessionCallback())
.build()
setListener(PlaybackServiceListener()) // irrelevant to this question
}
private inner class CustomMediaSessionCallback: MediaSession.Callback {
override fun onConnect(
session: MediaSession,
controller: MediaSession.ControllerInfo
): MediaSession.ConnectionResult {
val sessionCommands = MediaSession.ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon()
.add(back.playerCommand)
.add(forward.playerCommand)
.build()
return MediaSession.ConnectionResult.AcceptedResultBuilder(session)
.setCustomLayout(buttons)
.setAvailablePlayerCommands(sessionCommands)
.build()
}
}
В моей ViewModel я создаю SessionToken
и MediaController
:
private val sessionToken =
SessionToken(getApplication(), ComponentName(getApplication(), PlaybackService::class.java))
private val controllerFuture = MediaController.Builder(getApplication(), sessionToken).buildAsync()
Когда MediaController
готов, я добавляю MediaItem
и начинаю воспроизведение.
with(controller!!) {
playWhenReady = true
setMediaItem(mediaItem)
prepare()
}
Чтобы внести ясность: абсолютно ничего не изменится, если я удалю .setCustomLayout(buttons)
и .setCallback(CustomMediaSessionCallback())
из своего MediaSession.Builder
. Это не имеет никакого эффекта.
Я получаю предупреждение в logcat: «Не удалось найти уникальный зарегистрированный приемник медиа-кнопки в данном контексте». Я искал эту строку в репозитории media3, но она там не появилась.
Может кто-нибудь мне помочь? Спасибо!
@gtxtreme Я отредактировал свое сообщение, указав, как я создаю SessionToken и MediaController в ViewModel. Если эти действия доступны по умолчанию, возможно, в том, как я создаю MediaItem
, чего-то не хватает или что-то в этом роде. Документы крайне бесполезны, несмотря ни на что...
Обновление! Я узнал, что CommandButton.Builder.setPlayerCommand() предназначен для PlayerView в приложении, а не для уведомления. Для уведомления нам нужно вызвать setSessionCommand() с нашей специальной командной строкой. Благодаря этому изменению я получаю функцию поиска вперед и назад, но при этом оба значка добавляются справа от панели поиска. Я нашел стили и файлы макета в репозитории media3, соответствующие этим кнопкам (например, ExoStyledControls.Button.Center.FfwdWithAmount
). Кажется, они предназначены только для пользовательского интерфейса приложения. Я совсем теряю голову, пытаясь понять, как сделать эту очень простую вещь 😩
Дальнейшие приключения в кроличьей норе отладки... Я подозреваю, что эти кнопки недоступны, потому что команда игрока COMMAND_GET_CURRENT_MEDIA_ITEM
недоступна после установки MediaItem
. Теперь пытаюсь понять, почему... Но isCurrentMediaItemSeekable()
возвращает false, если эта команда не доступна.
Я наконец нашел на GitHub базу кода с пользовательским интерфейсом уведомлений, который я пытаюсь создать, и похоже, что люди действительно используют это сами. Мне кажется совершенно сумасшедшим, что это недоступно из коробки, но на данный момент я закончил копаться во внутренностях этой мусорной библиотеки. Пора построить макет с нуля и двигаться дальше по жизни.
Если я ошибаюсь, пожалуйста, дайте мне знать, и я буду рад принять лучший ответ.
Какая настройка по умолчанию. Судя по тому, что я прочитал, все эти действия доступны по умолчанию.