fix(app): Spotify resumed wieder nach TTS — nudgeMediaResume mit TRANSIENT
Stefan-Bug-Report: ARIA liest Nachricht vor, Spotify pausiert korrekt, ARIA spricht durch — aber Spotify spielt danach NICHT automatisch weiter. Sollte mit GAIN_TRANSIENT auto-resumen, tut es aber bei manchen Spotify-Versionen/Geraeten nicht zuverlaessig. Hintergrund: alte kickReleaseMedia() mit AUDIOFOCUS_GAIN (permanent) war zu aggressiv (Spotify interpretierte als "user stoppte" = Auto-Resume kaputt). Wurde entfernt. Jetzt ist das Pendel andersrum zu weit: ohne Nudge keine Resume. Sanfter Mittelweg: nudgeMediaResume() mit GAIN_TRANSIENT statt GAIN-permanent. 100ms hold, abandon. Spotify bekommt Focus-Wechsel- Hint ohne "user stopped"-Effekt. audio.ts: nach AudioFocus.release() 50ms warten, dann nudgeMediaResume. AudioFocusModule.kt: neue Methode + alte kickReleaseMedia bleibt mit ⚠️-Markierung fuer andere Use-Cases. APK neu bauen erforderlich. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -131,6 +131,58 @@ class AudioFocusModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
promise.resolve(true)
|
promise.resolve(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sanfter Spotify-Resume-Nudge: kurz USAGE_MEDIA mit TRANSIENT
|
||||||
|
* requesten und sofort abandonen. Spotify bekommt das als
|
||||||
|
* Focus-Frei-Signal und resumed automatisch — aber weil TRANSIENT
|
||||||
|
* (nicht GAIN permanent), interpretiert Spotify das NICHT als
|
||||||
|
* "user stopped" was Auto-Resume verhindert haette.
|
||||||
|
*
|
||||||
|
* Hintergrund: ARIA spricht TTS via USAGE_ASSISTANT GAIN_TRANSIENT,
|
||||||
|
* Spotify pausiert. ARIA released. Spotify SOLLTE nach
|
||||||
|
* TRANSIENT-Loss + Abandon automatisch resumen, tut es aber bei
|
||||||
|
* manchen Versionen / Geraeten nicht zuverlaessig. Dieser Nudge
|
||||||
|
* triggert den Focus-Stack-Refresh ohne den Spotify-Auto-Stop-Bug
|
||||||
|
* der alten kickReleaseMedia mit GAIN permanent.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
fun nudgeMediaResume(promise: Promise) {
|
||||||
|
val am = audioManager()
|
||||||
|
if (am == null) {
|
||||||
|
promise.resolve(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Thread {
|
||||||
|
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 nudgeListener = AudioManager.OnAudioFocusChangeListener { /* ignorieren */ }
|
||||||
|
val nudgeReq = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
|
||||||
|
.setAudioAttributes(attrs)
|
||||||
|
.setOnAudioFocusChangeListener(nudgeListener)
|
||||||
|
.build()
|
||||||
|
am.requestAudioFocus(nudgeReq)
|
||||||
|
Thread.sleep(100)
|
||||||
|
am.abandonAudioFocusRequest(nudgeReq)
|
||||||
|
} else {
|
||||||
|
val nudgeListener = AudioManager.OnAudioFocusChangeListener { /* ignorieren */ }
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
am.requestAudioFocus(nudgeListener, AudioManager.STREAM_MUSIC,
|
||||||
|
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
|
||||||
|
Thread.sleep(100)
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
am.abandonAudioFocus(nudgeListener)
|
||||||
|
}
|
||||||
|
Log.i(TAG, "nudgeMediaResume: USAGE_MEDIA TRANSIENT request+abandon (Spotify-Resume-Trigger)")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "nudgeMediaResume failed: ${e.message}")
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
promise.resolve(true)
|
||||||
|
}
|
||||||
|
|
||||||
/** Den USAGE_MEDIA-Focus-Stack im System aufmischen, damit Spotify/YouTube
|
/** Den USAGE_MEDIA-Focus-Stack im System aufmischen, damit Spotify/YouTube
|
||||||
* resumen wenn ein anderer Player (z.B. react-native-sound) seinen Focus
|
* resumen wenn ein anderer Player (z.B. react-native-sound) seinen Focus
|
||||||
* nicht ordnungsgemaess released hat. Strategie: kurz selbst USAGE_MEDIA
|
* nicht ordnungsgemaess released hat. Strategie: kurz selbst USAGE_MEDIA
|
||||||
@@ -140,6 +192,10 @@ class AudioFocusModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
*
|
*
|
||||||
* Workaround fuer das react-native-sound-Bug: Sound.stop()/release()
|
* Workaround fuer das react-native-sound-Bug: Sound.stop()/release()
|
||||||
* laesst den AudioFocusRequest haengen.
|
* laesst den AudioFocusRequest haengen.
|
||||||
|
*
|
||||||
|
* ⚠️ ACHTUNG: nutzt AUDIOFOCUS_GAIN (permanent), Spotify kann das als
|
||||||
|
* "user-action stopp" interpretieren und Auto-Resume verhindern.
|
||||||
|
* Fuer Spotify-Resume nach TTS lieber nudgeMediaResume() nehmen (sanfter).
|
||||||
*/
|
*/
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun kickReleaseMedia(promise: Promise) {
|
fun kickReleaseMedia(promise: Promise) {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const { AudioFocus, PcmStreamPlayer } = NativeModules as {
|
|||||||
AudioFocus?: {
|
AudioFocus?: {
|
||||||
requestDuck: () => Promise<boolean>;
|
requestDuck: () => Promise<boolean>;
|
||||||
requestExclusive: () => Promise<boolean>;
|
requestExclusive: () => Promise<boolean>;
|
||||||
|
nudgeMediaResume: () => Promise<boolean>;
|
||||||
release: () => Promise<boolean>;
|
release: () => Promise<boolean>;
|
||||||
kickReleaseMedia: () => Promise<boolean>;
|
kickReleaseMedia: () => Promise<boolean>;
|
||||||
getMode?: () => Promise<number>;
|
getMode?: () => Promise<number>;
|
||||||
@@ -332,6 +333,13 @@ class AudioService {
|
|||||||
}
|
}
|
||||||
console.log('[Audio] AudioFocus jetzt released');
|
console.log('[Audio] AudioFocus jetzt released');
|
||||||
AudioFocus?.release().catch(() => {});
|
AudioFocus?.release().catch(() => {});
|
||||||
|
// Spotify-Resume-Trigger: nach Abandon den USAGE_MEDIA-Focus-Stack
|
||||||
|
// mit kurzem TRANSIENT-Nudge aufmischen. Spotify resumed sonst bei
|
||||||
|
// manchen Versionen / Geraeten nicht zuverlaessig nach Auto-Loss.
|
||||||
|
// 50ms Delay damit das Abandon erst durch ist.
|
||||||
|
setTimeout(() => {
|
||||||
|
AudioFocus?.nudgeMediaResume().catch(() => {});
|
||||||
|
}, 50);
|
||||||
}, this.FOCUS_RELEASE_DELAY_MS);
|
}, this.FOCUS_RELEASE_DELAY_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user