Обратные вызовы Android MediaSessionCompat не срабатывают

Я создаю проигрыватель аудиокниг и использую классы, связанные с MediaSessionCompat, для обработки уведомлений. Мой код во многом основан на примерах android-MediaBrowserService ( https://github.com/googlearchive/android-MediaBrowserService), и на данный момент я не совсем понимаю все это (особенно createContentIntent)

Вот мой простой класс, отвечающий за создание уведомлений от bookPlayer, предоставляющий метаданные и данные состояния воспроизведения.

class PlayerNotificationManager(val playerService: PlayerService) {

    val CHANNEL_ID = "pylvain.gamma"
    val NOTIFICATION_ID = 412
    private val REQUEST_CODE = 501

    val notificationManager =
        playerService.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

    private val playAction: NotificationCompat.Action =
        NotificationCompat.Action (
            android.R.drawable.ic_media_pause,
            "PAUSE",
            buildMediaButtonPendingIntent(
                playerService,
                PlaybackStateCompat.ACTION_PAUSE
            )
        )

    fun getNotification(bookPlayer: BookPlayer): Notification  {

        if (isAndroidOOrHigher()) createChannel()

        val style = androidx.media.app.NotificationCompat.MediaStyle()
            .setMediaSession(playerService.sessionToken)
            .setShowCancelButton(true)
            .setShowActionsInCompactView(0)
            .setCancelButtonIntent(
                buildMediaButtonPendingIntent(
                    playerService,
                    PlaybackStateCompat.ACTION_STOP
                )
            )

        val builder = NotificationCompat.Builder(playerService, CHANNEL_ID)
            .addAction(playAction)
            .setStyle(style)
            .setSmallIcon(R.drawable.ic_music_rest_quarter)
            .setContentIntent(createContentIntent())
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

        return builder.build()

    }

    private fun createContentIntent(): PendingIntent { //TODO: Understand that
        Timber.i("Creating Intent")
        val openUI = Intent(playerService, MainActivity::class.java)
        openUI.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
        return PendingIntent.getActivity(
            playerService, 0, openUI, PendingIntent.FLAG_UPDATE_CURRENT
        )
    }

Уведомление отлично отображается с метаданными

Вот мой MediaBrowserService, обрабатывающий сеанс мультимедиа, где я зарегистрировал обратные вызовы. Книжный проигрыватель сконструирован и вставлен с помощью Koin. :

class PlayerService : MediaBrowserServiceCompat() {

    private lateinit var playerNotificationManager: PlayerNotificationManager
    lateinit var session: MediaSessionCompat

    private val bookPlayer: BookPlayer by inject()

    override fun onCreate() {
        super.onCreate()

        session = MediaSessionCompat(this, "MusicService")
        session.apply {
            setFlags(
                MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
                        MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
            )
            setPlaybackState(bookPlayer.playbackState)
            setMetadata(bookPlayer.getMetadata())
            setCallback(callbacks)
            setActive(true)
        }

        setSessionToken(session.sessionToken)
        playerNotificationManager = PlayerNotificationManager(this)

        val notification = playerNotificationManager.getNotification(bookPlayer)

        startForeground(playerNotificationManager.NOTIFICATION_ID, notification)

    }

    override fun onTaskRemoved(rootIntent: Intent?) { //TODO
        super.onTaskRemoved(rootIntent)
        stopSelf()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onGetRoot(
        clientPackageName: String,
        clientUid: Int,
        rootHints: Bundle?
    ): BrowserRoot? {
        return BrowserRoot("root", null)
    }

    override fun onLoadChildren(
        parentId: String, result: Result<MutableList<MediaBrowserCompat.MediaItem>>
    ) {
        result.sendResult(null);
    }
    
    override fun onDestroy() {
        session.release()
    }


    val callbacks = object : MediaSessionCompat.Callback() {

        override fun onCommand(command: String?, extras: Bundle?, cb: ResultReceiver?) {
            Timber.i("Test")
            super.onCommand(command, extras, cb)
        }

        override fun onPrepare() {
            Timber.i("Preparing")
        }

        override fun onPlay() {
            Timber.i("Playing")
            bookPlayer.pause()
        }

        override fun onPause() {
            Timber.i("Pausing")
            bookPlayer.pause()
        }

        override fun onSkipToNext() {}
        override fun onSkipToPrevious() {}
        override fun onStop() {}
        override fun onSeekTo(pos: Long) {}
        override fun onMediaButtonEvent(mediaButtonIntent: Intent): Boolean = true

    }

}

Затем служба запускается из основного действия с

startService(Intent(mainContext, PlayerService::class.java))

Я также добавил это в свой манифест Android


        <service android:name=".playerservice.PlayerService">
            <intent-filter>
                <action android:name="android.media.browse.MediaBrowserService" />
            </intent-filter>
        </service>


        <receiver android:name="androidx.media.session.MediaButtonReceiver">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </receiver>

Ни один из обратных вызовов не вызывается всякий раз, когда я нажимаю кнопку. когда я это делаю, приложение регистрирует следующий текст:

D/MediaBrowserCompat: Connecting to a MediaBrowserService.

и ничего не происходит ... Я обыскал весь интернет и совершенно ничего не знаю, но это наверняка что-то простое. Кто-нибудь может мне помочь ? Заранее большое спасибо ‹3


person Pylvain    schedule 20.01.2021    source источник


Ответы (2)


Обратный вызов сработал ... Просто не так, как предполагалось. Оказывается, кнопка play action звонила

override fun onMediaButtonEvent(mediaButtonIntent: Intent): Boolean = true

Удалил функцию, и... Работает...

Спасибо за Ваше внимание !

person Pylvain    schedule 20.01.2021
comment
документация по работе с медиа-кнопками на самом деле специально вызывает Android автоматически отправляет события кнопки мультимедиа в ваш активный сеанс мультимедиа, вызывая onMediaButtonEvent(). По умолчанию этот обратный вызов преобразует KeyEvent в соответствующий метод обратного вызова мультимедийного сеанса, который соответствует коду ключа. — вы удалили реализацию по умолчанию, так что да, вы не получите поведение по умолчанию. - person ianhanniballake; 20.01.2021
comment
Извините, я был немного соленым. Мне потребовались часы, чтобы найти эту ошибку. Это было нелегко заметить - person Pylvain; 20.01.2021

Если вы хотите получить медиа-кнопку, вы должны что-то воспроизвести. Попробуйте воспроизвести фиктивный звук, когда ваш сервис запущен

    // play dummy audio
    AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, 48000, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT,
            AudioTrack.getMinBufferSize(48000, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT), AudioTrack.MODE_STREAM);
    at.play();

    // a little sleep 
    at.stop();
    at.release();

https://stackoverflow.com/a/50678833/9891730 - это мой ответ до

person 오효민    schedule 24.03.2021