feat: native Android audio via Media3 ExoPlayer + full NSBridge JS adapter

- Replace legacy MediaBrowserServiceCompat/MediaSessionCompat with
  Media3 MediaSessionService (MusicService.kt) + ExoPlayer set to
  WAKE_MODE_NETWORK — holds WakeLock + WifiLock through Doze so audio
  plays uninterrupted on locked screen
- Media3 manages the notification; no manual NotificationCompat.Builder
  → no disappear/reappear flicker when skipping tracks
- Full bidirectional bridge:
    JS → native: window.NSBridge.* (WebBridge.kt, 13 methods)
    Native → JS: window.NSPlayer.* callbacks (onReady, onTrackChanged,
      onPositionUpdate, onPlaybackStateChanged, onQueueEnded, onError)
- player.js: progressive enhancement — all playback routes through
  NSBridge when running inside the wrapper, falls back to S.audio on
  plain PWA/browser; removes all window.Android.* calls
- main.js: guard _onForeground mediaSession playbackState setter behind
  !window.NSBridge so native Media3 session is never stomped
- ProGuard rules updated to keep WebBridge @JavascriptInterface methods
  and Media3 classes
- bridge-test.html: manual integration test page for every bridge method

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-05-05 01:14:28 +02:00
1 parent 1ede8727a2
commit 0e17d2e5db
15 files changed
+1093 -95

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
[versions]
agp = "8.13.2"
kotlin = "1.9.23"
coreKtx = "1.13.1"
appcompat = "1.7.0"
material = "1.12.0"
media3 = "1.4.1"
coroutines = "1.8.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }
media3-session = { group = "androidx.media3", name = "media3-session", version.ref = "media3" }
media3-common = { group = "androidx.media3", name = "media3-common", version.ref = "media3" }
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }