fix(audio): Spotify resumed nach Mute — RNSound's haengenden Focus loesen
Logs zeigten: react-native-sound requestet beim Sound.play() einen EIGENEN AudioFocus mit USAGE_MEDIA, released den aber bei Sound.stop()/ release() NICHT (bekanntes RN-sound-Bug). Spotify sieht den haengenden Media-Focus → bleibt pausiert. Workaround: Native-Methode kickReleaseMedia() macht einen request+abandon- Cycle mit USAGE_MEDIA, das System raeumt damit den Focus-Stack auf und Spotify bekommt sauberen GAIN-Event. stopPlayback ruft das jetzt nach Sound.release() wenn vorher ein RNSound aktiv war. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -131,6 +131,52 @@ class AudioFocusModule(reactContext: ReactApplicationContext) : ReactContextBase
|
||||
promise.resolve(true)
|
||||
}
|
||||
|
||||
/** Den USAGE_MEDIA-Focus-Stack im System aufmischen, damit Spotify/YouTube
|
||||
* resumen wenn ein anderer Player (z.B. react-native-sound) seinen Focus
|
||||
* nicht ordnungsgemaess released hat. Strategie: kurz selbst USAGE_MEDIA
|
||||
* GAIN beanspruchen — das System invalidiert dabei den haengenden Stack-
|
||||
* Eintrag des anderen Players — und sofort wieder abandonen. Spotify
|
||||
* bekommt den Focus-Gain und resumed.
|
||||
*
|
||||
* Workaround fuer das react-native-sound-Bug: Sound.stop()/release()
|
||||
* laesst den AudioFocusRequest haengen.
|
||||
*/
|
||||
@ReactMethod
|
||||
fun kickReleaseMedia(promise: Promise) {
|
||||
val am = audioManager()
|
||||
if (am == null) {
|
||||
promise.resolve(false)
|
||||
return
|
||||
}
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val attrs = AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
||||
.build()
|
||||
val kickListener = AudioManager.OnAudioFocusChangeListener { /* ignorieren */ }
|
||||
val kickReq = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
||||
.setAudioAttributes(attrs)
|
||||
.setOnAudioFocusChangeListener(kickListener)
|
||||
.build()
|
||||
am.requestAudioFocus(kickReq)
|
||||
am.abandonAudioFocusRequest(kickReq)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
val kickListener = AudioManager.OnAudioFocusChangeListener { /* ignorieren */ }
|
||||
@Suppress("DEPRECATION")
|
||||
am.requestAudioFocus(kickListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
|
||||
@Suppress("DEPRECATION")
|
||||
am.abandonAudioFocus(kickListener)
|
||||
}
|
||||
Log.i(TAG, "kickReleaseMedia: USAGE_MEDIA-Stack aufgemischt")
|
||||
promise.resolve(true)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "kickReleaseMedia failed: ${e.message}")
|
||||
promise.resolve(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun release() {
|
||||
val am = audioManager() ?: return
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
||||
@@ -41,6 +41,8 @@ const { AudioFocus, PcmStreamPlayer } = NativeModules as {
|
||||
requestDuck: () => Promise<boolean>;
|
||||
requestExclusive: () => Promise<boolean>;
|
||||
release: () => Promise<boolean>;
|
||||
kickReleaseMedia: () => Promise<boolean>;
|
||||
getMode?: () => Promise<number>;
|
||||
};
|
||||
PcmStreamPlayer?: {
|
||||
start: (sampleRate: number, channels: number, prerollSeconds: number) => Promise<boolean>;
|
||||
@@ -1196,6 +1198,10 @@ class AudioService {
|
||||
stopBackgroundAudio().catch(() => {});
|
||||
this.audioQueue = [];
|
||||
this.isPlaying = false;
|
||||
// Merken: war ein react-native-sound-Sound aktiv? Dann muessen wir nach
|
||||
// release() den Focus-Stack aufmischen (RNSound-Bug: stop+release laesst
|
||||
// den AudioFocusRequest haengen, Spotify resumed sonst nicht).
|
||||
const hadRnSound = !!(this.currentSound || this.resumeSound || this.preloadedSound);
|
||||
if (this.currentSound) {
|
||||
this.currentSound.stop();
|
||||
this.currentSound.release();
|
||||
@@ -1224,6 +1230,11 @@ class AudioService {
|
||||
// Audio-Focus sofort freigeben — User hat explizit abgebrochen
|
||||
this._cancelDeferredFocusRelease();
|
||||
AudioFocus?.release().catch(() => {});
|
||||
if (hadRnSound) {
|
||||
// RNSound's haengender USAGE_MEDIA-Focus aufloesen — sonst bleibt
|
||||
// Spotify pausiert obwohl unser Focus released ist.
|
||||
AudioFocus?.kickReleaseMedia?.().catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
// --- Status & Callbacks ---
|
||||
|
||||
Reference in New Issue
Block a user