Media3: Как динамически обновлять уведомление MediaSession для пользовательского звукового микшера?Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Media3: Как динамически обновлять уведомление MediaSession для пользовательского звукового микшера?

Сообщение Anonymous »

Я разрабатываю приложение для Android с использованием Kotlin и Jetpack Compose. Это звуковой микшер, в котором одновременно воспроизводятся несколько звуковых дорожек (циклов).
У меня есть AudioMixerRepository, содержащий карту нескольких экземпляров ExoPlayer (по одному для каждого звука) для их одновременного воспроизведения. Когда пользователь выбирает звук, он сразу же начинает воспроизводиться в приложении.
Я управляю жизненным циклом этих треков в своей AudioMixerViewModel:

Код: Выделить всё

/**
* When app goes to background and session is not active, pause all the tracks.
*/
override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)

if (!uiState.value.isSessionActive) {
audioMixerRepository.pauseAll()
}
}

/**
* When app goes to foreground, resume all the tracks.
*/
override fun onStart(owner: LifecycleOwner) {
super.onStart(owner)

if (!uiState.value.isSessionActive) {
audioMixerRepository.resumeAll()
}
}

/**
* When viewModel is destroyed, release all the tracks.
*/
override fun onCleared() {
super.onCleared()
ProcessLifecycleOwner.get().lifecycle.removeObserver(this)
if (!uiState.value.isSessionActive) {
audioMixerRepository.releaseAll()
}
}
Если пользователь нажимает кнопку «Пуск», сеанс начинается, и звук должен воспроизводиться в фоновом режиме (это работает прямо сейчас). Кроме того, должно появиться системное уведомление Media3 с названием и изображением выбранного микса.
Чтобы добиться этого и поддерживать активность сеанса в фоновом режиме, я реализовал MediaSessionService. Поскольку фактическое микширование звука (несколько экземпляров ExoPlayer) управляется моим репозиторием, я использую "фиктивный проигрыватель" внутри службы. Этот проигрыватель загружает беззвучную звуковую дорожку исключительно для поддержания состояния MediaSession, запуска службы переднего плана (только при нажатии кнопки «Пуск») и отображения системного уведомления. Я использую ForwardingPlayer для перехвата действий воспроизведения/паузы и направления их в свой репозиторий.
Вот мой MediaSessionService:

Код: Выделить всё

class AudioSessionMediaService : MediaSessionService() {

private var mediaSession: MediaSession? = null
private var dummyPlayer: ExoPlayer? = null

private val audioRepository: AudioMixerRepository by inject()

@OptIn(UnstableApi::class)
override fun onCreate() {
super.onCreate()

// 1. Create a dummy player. Its only purpose is to maintain the state of MediaSession
dummyPlayer = ExoPlayer.Builder(this).build().apply {
volume = 0f
repeatMode = Player.REPEAT_MODE_ALL

val rawResourceId = R.raw.waves1
val soundUri = Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.path(rawResourceId.toString()).build()

val mediaItem = MediaItem.Builder()
.setUri(soundUri)
.build()

setMediaItem(mediaItem)
prepare()
}

// 2.  Intercept commands
val forwardingPlayer = object : ForwardingPlayer(dummyPlayer!!) {
override fun getAvailableCommands(): Player.Commands {
return super.getAvailableCommands().buildUpon()
.remove(COMMAND_SEEK_TO_NEXT)
.remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
.remove(COMMAND_SEEK_TO_PREVIOUS)
.remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
.build()
}

override fun getDuration(): Long {
return C.TIME_UNSET
}

override fun play() {
super.play()
audioRepository.fadeInAllSounds()
}

override fun pause() {
super.pause()
audioRepository.fadeOutAndPauseAllSounds()
}

override fun stop() {
super.stop()
audioRepository.fadeOutAndPauseAllSounds()
}
}

val callback = object : MediaSession.Callback {
override fun onAddMediaItems(
mediaSession: MediaSession,
controller: MediaSession.ControllerInfo,
mediaItems: MutableList,
): ListenableFuture {
val resolvedItems = mediaItems.map { item ->
item.buildUpon()
.setMediaId(item.mediaId)
.setUri(item.localConfiguration?.uri)
.setMediaMetadata(item.mediaMetadata)
.build()
}.toMutableList()

return Futures.immediateFuture(resolvedItems)
}
}

// 3. Create session
mediaSession = MediaSession.Builder(this, forwardingPlayer)
.setId("MultitrackAudioSession")
.setCallback(callback)
.build()
}

override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
mediaSession

override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}

super.onDestroy()
}
}
Проблема
Я управляю подключением к службе с помощью синглтона AudioSessionManager. Я инициализирую MediaController асинхронно при запуске этого синглтона. Когда пользователь нажимает «Пуск» в пользовательском интерфейсе, я использую этот MediaController для отправки нового MediaItem с настраиваемым заголовком и изображением выбранного микса:

Код: Выделить всё

class AudioSessionManager(private val context: Context) {

private var mediaControllerFuture: ListenableFuture? = null
private var mediaController: MediaController? = null

init {
val sessionToken = SessionToken(
context,
ComponentName(context, AudioSessionMediaService::class.java)
)
mediaControllerFuture = MediaController.Builder(context, sessionToken).buildAsync()
mediaControllerFuture?.addListener(
{ mediaController = mediaControllerFuture?.get() },
ContextCompat.getMainExecutor(context)
)
}

fun startSession(mixTitle: String, mixImageUri: Uri) {
val rawResourceId = R.raw.waves2
val dummyUri = Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.path(rawResourceId.toString()).build()

val mediaItem = MediaItem.Builder()
.setMediaId("mix-${System.currentTimeMillis()}") // Trying to force an update with a unique ID
.setUri(dummyUri)
.setMediaMetadata(
MediaMetadata.Builder()
.setTitle(mixTitle)
.setArtworkUri(mixImageUri)
.build()
)
.build()

mediaController?.let { controller ->
if (controller.mediaItemCount > 0) {
controller.replaceMediaItem(0, mediaItem)
} else {
controller.setMediaItem(mediaItem)
}
controller.prepare()
controller.play()
}
}
}
Прямо сейчас уведомление появляется, когда инициализируется синглтон (служба подключается) и заголовок уведомления и обложка не обновляются, когда я нажимаю «Пуск». Система, кажется, игнорирует мои MediaMetadata или возвращается к тому, что она извлекает (или не извлекает) из необработанного аудиофайла без звука.
Я знаю о MediaNotification.Provider, но я не реализовал его, поскольку в официальной документации Android указано, что пользовательские уведомления через поставщика игнорируются в API 33+.
Мои вопросы:
  • Как заставить системное уведомление Media3 в API 33+ отражать пользовательские MediaMetadata (заголовок и обложку), отправленные через MediaController?
  • Как можно Я запрещаю отображение уведомления при инициализации, чтобы оно появлялось только, когда пользователь нажимает кнопку «Пуск»?


Подробнее здесь: https://stackoverflow.com/questions/798 ... om-sound-m
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»