Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Android expanded notification controls do not emit events because MEDIA_BUTTON intents are not handled

オープン 初心者向け
#1,273 コメント 5 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
82/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
android, kotlin, react-native
領域
mobile-dev

調査の方向性

android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotificationReceiver.kt から始め、そこで処理されるアクションと MediaSessionManager.kt の MEDIA_BUTTON 登録を比較します。Intent.EXTRA_KEY_EVENT の経路を追跡し、ネイティブ Android の再ビルド後に、展開されたコントロールとロック画面のコントロールが期待される再生通知イベントを発行することを確認します。

索引モデルが issue の本文から書いたものです。

説明

android bug

Summary

On Android with [email protected], playback controls behave differently depending on which notification UI is used:

  • Compact notification widget controls work.
  • Scrubbing works.
  • Expanded notification / lock-screen play, pause, next, and previous controls do not emit the expected playbackNotification* events.
  • In a high-frequency update path, we also observed a java.util.ConcurrentModificationException from NotificationCompatBuilder while the notification was being updated.

I searched existing issues/PRs for MEDIA_BUTTON, PlaybackNotificationReceiver, expanded notification controls, and ConcurrentModificationException NotificationCompatBuilder, but did not find an existing report for this exact Android path.

Important MRE note

I know the issue template strongly encourages a minimal reproducible example. This report is currently based on a real app reproduction, not a public MRE yet. I am filing now because the code-level cause appears small and concrete. If needed, I can follow up with a minimal public repro project that exercises only PlaybackNotificationManager.show, enableControl, and event listeners.

Environment

  • react-native-audio-api: 0.13.3
  • React Native: 0.81.5
  • Expo SDK: 54
  • Android: reproduced on Android device/build from an Expo React Native app
  • New Architecture enabled
  • react-native-worklets: 0.6.1

Reproduction observed in app

  1. Configure and show playback notification metadata.
  2. Enable controls:
    • play
    • pause
    • nextTrack
    • previousTrack
    • skipForward
    • skipBackward
    • seekTo
  3. Start playback.
  4. Swipe down to show the compact notification widget.
  5. Press controls from the compact widget.
  6. Expand the notification or use lock-screen media controls.
  7. Press play/pause/next/previous.

Expected behavior

Expanded notification and lock-screen play/pause/next/previous should emit the same events as the compact notification widget:

  • playbackNotificationPlay
  • playbackNotificationPause
  • playbackNotificationNextTrack
  • playbackNotificationPreviousTrack

Actual behavior

Compact widget controls work, but expanded notification / lock-screen play/pause/next/previous controls do not emit the expected events. Scrubbing still works.

Code-level analysis

The compact controls appear to work through MediaSessionCompat.Callback in PlaybackNotification.kt:

override fun onPlay() {
  audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_PLAY.ordinal, mapOf())
}

override fun onPause() {
  audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_PAUSE.ordinal, mapOf())
}

override fun onSkipToNext() {
  audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_NEXT_TRACK.ordinal, mapOf())
}

override fun onSkipToPrevious() {
  audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_PREVIOUS_TRACK.ordinal, mapOf())
}

But expanded notification actions are created as pending intents with action PlaybackNotification.MEDIA_BUTTON:

val intent = Intent(MEDIA_BUTTON)
intent.setPackage(context.packageName)
intent.putExtra(Intent.EXTRA_KEY_EVENT, KeyEvent(KeyEvent.ACTION_DOWN, keyCode))

MediaSessionManager.kt registers that action on PlaybackNotificationReceiver:

playbackFilter.addAction(PlaybackNotification.MEDIA_BUTTON)

However, PlaybackNotificationReceiver.kt handles only:

  • ACTION_NOTIFICATION_DISMISSED
  • ACTION_SKIP_FORWARD
  • ACTION_SKIP_BACKWARD

It does not handle PlaybackNotification.MEDIA_BUTTON or decode Intent.EXTRA_KEY_EVENT.

That seems to explain the split behavior: compact MediaSession callbacks work, while expanded PendingIntent-backed media button actions are dropped.

Local patch that fixed the app behavior

This local patch maps MEDIA_BUTTON key events to the same AudioEvent values as the MediaSession callback path:

diff --git a/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotificationReceiver.kt b/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotificationReceiver.kt
--- a/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotificationReceiver.kt
+++ b/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotificationReceiver.kt
@@ -3,6 +3,7 @@ package com.swmansion.audioapi.system.notification
 import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
+import android.view.KeyEvent
 import com.swmansion.audioapi.AudioAPIModule
 import com.swmansion.audioapi.system.AudioEvent
 
@@ -40,6 +41,43 @@ class PlaybackNotificationReceiver : BroadcastReceiver() {
         val body = HashMap<String, Any>().apply { put("value", 15) }
         audioAPIModule?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_SKIP_BACKWARD.ordinal, body)
       }
+
+      PlaybackNotification.MEDIA_BUTTON -> {
+        val keyEvent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
+          intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT, KeyEvent::class.java)
+        } else {
+          @Suppress("DEPRECATION")
+          intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)
+        }
+
+        if (keyEvent?.action != KeyEvent.ACTION_DOWN) return
+
+        when (keyEvent.keyCode) {
+          KeyEvent.KEYCODE_MEDIA_PLAY -> {
+            audioAPIModule?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_PLAY.ordinal, mapOf())
+          }
+          KeyEvent.KEYCODE_MEDIA_PAUSE -> {
+            audioAPIModule?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_PAUSE.ordinal, mapOf())
+          }
+          KeyEvent.KEYCODE_MEDIA_STOP -> {
+            audioAPIModule?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_STOP.ordinal, mapOf())
+          }
+          KeyEvent.KEYCODE_MEDIA_NEXT -> {
+            audioAPIModule?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_NEXT_TRACK.ordinal, mapOf())
+          }
+          KeyEvent.KEYCODE_MEDIA_PREVIOUS -> {
+            audioAPIModule?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_PREVIOUS_TRACK.ordinal, mapOf())
+          }
+          KeyEvent.KEYCODE_MEDIA_FAST_FORWARD -> {
+            val body = HashMap<String, Any>().apply { put("value", 15) }
+            audioAPIModule?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_SKIP_FORWARD.ordinal, body)
+          }
+          KeyEvent.KEYCODE_MEDIA_REWIND -> {
+            val body = HashMap<String, Any>().apply { put("value", 15) }
+            audioAPIModule?.invokeHandlerWithEventNameAndEventBody(AudioEvent.PLAYBACK_NOTIFICATION_SKIP_BACKWARD.ordinal, body)
+          }
+        }
+      }
     }
   }
 }

Related crash observed

Before serializing notification updates in app code, we also saw this Android crash while notification controls/metadata were being updated:

java.util.ConcurrentModificationException
  java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1112)
  java.util.ArrayList$Itr.next(ArrayList.java:1065)
  androidx.core.app.NotificationCompatBuilder.<init>(NotificationCompatBuilder.java:127)
  androidx.core.app.NotificationCompat$Builder.build(NotificationCompat.java:2528)
  com.swmansion.audioapi.system.notification.PlaybackNotification.buildNotification(PlaybackNotification.kt:515)
  com.swmansion.audioapi.system.notification.PlaybackNotification.updateInternal$lambda$1$lambda$0(PlaybackNotification.kt:249)

We mitigated that separately in app code by serializing show, hide, and enableControl calls, so the main upstream issue here is the missing MEDIA_BUTTON handling path.

Cache / rebuild status

This was tested after rebuilding the native Android app. The Expo/Metro cache was also reset during upgrade testing. The local patch requires a native rebuild to take effect.

主要言語
C++
スター
848
フォーク
95
平均マージ
3日 19時間
マージ済み PR(30日)
30

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

software-mansion/react-native-audio-api のほかの issue

software-mansion/react-native-audio-api の issue をすべて見る

似ている issue

C++ の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。