Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 08857093b5 | |||
| 62018b3e51 | |||
| 89e3a195a3 | |||
| f023ba0ac5 | |||
| a0570ef8f7 | |||
| facde1fef7 | |||
| 38106a2096 | |||
| a476afb311 |
@@ -79,8 +79,8 @@ android {
|
|||||||
applicationId "com.ariacockpit"
|
applicationId "com.ariacockpit"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 10005
|
versionCode 10009
|
||||||
versionName "0.1.0.5"
|
versionName "0.1.0.9"
|
||||||
// Fallback fuer Libraries mit Product Flavors
|
// Fallback fuer Libraries mit Product Flavors
|
||||||
missingDimensionStrategy 'react-native-camera', 'general'
|
missingDimensionStrategy 'react-native-camera', 'general'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,33 +148,39 @@ class AudioFocusModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
promise.resolve(false)
|
promise.resolve(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
// Async laufen lassen — wir wollen einen request, Pause, dann abandon.
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
// Ohne Pause merkt das System (und damit Spotify) die kurze Owner-
|
||||||
val attrs = AudioAttributes.Builder()
|
// Wechsel oft gar nicht. 250ms reicht erfahrungsgemaess fuer den
|
||||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
// Focus-Stack-Refresh.
|
||||||
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
Thread {
|
||||||
.build()
|
try {
|
||||||
val kickListener = AudioManager.OnAudioFocusChangeListener { /* ignorieren */ }
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val kickReq = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
val attrs = AudioAttributes.Builder()
|
||||||
.setAudioAttributes(attrs)
|
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||||
.setOnAudioFocusChangeListener(kickListener)
|
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
||||||
.build()
|
.build()
|
||||||
am.requestAudioFocus(kickReq)
|
val kickListener = AudioManager.OnAudioFocusChangeListener { /* ignorieren */ }
|
||||||
am.abandonAudioFocusRequest(kickReq)
|
val kickReq = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
||||||
} else {
|
.setAudioAttributes(attrs)
|
||||||
@Suppress("DEPRECATION")
|
.setOnAudioFocusChangeListener(kickListener)
|
||||||
val kickListener = AudioManager.OnAudioFocusChangeListener { /* ignorieren */ }
|
.build()
|
||||||
@Suppress("DEPRECATION")
|
am.requestAudioFocus(kickReq)
|
||||||
am.requestAudioFocus(kickListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
|
Thread.sleep(250)
|
||||||
@Suppress("DEPRECATION")
|
am.abandonAudioFocusRequest(kickReq)
|
||||||
am.abandonAudioFocus(kickListener)
|
} else {
|
||||||
|
val kickListener = AudioManager.OnAudioFocusChangeListener { /* ignorieren */ }
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
am.requestAudioFocus(kickListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
|
||||||
|
Thread.sleep(250)
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
am.abandonAudioFocus(kickListener)
|
||||||
|
}
|
||||||
|
Log.i(TAG, "kickReleaseMedia: USAGE_MEDIA-Stack aufgemischt (250ms Pause)")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "kickReleaseMedia failed: ${e.message}")
|
||||||
}
|
}
|
||||||
Log.i(TAG, "kickReleaseMedia: USAGE_MEDIA-Stack aufgemischt")
|
}.start()
|
||||||
promise.resolve(true)
|
promise.resolve(true)
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, "kickReleaseMedia failed: ${e.message}")
|
|
||||||
promise.resolve(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun release() {
|
private fun release() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "aria-cockpit",
|
"name": "aria-cockpit",
|
||||||
"version": "0.1.0.5",
|
"version": "0.1.0.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"android": "react-native run-android",
|
"android": "react-native run-android",
|
||||||
|
|||||||
@@ -868,11 +868,16 @@ class AudioService {
|
|||||||
final?: boolean;
|
final?: boolean;
|
||||||
silent?: boolean;
|
silent?: boolean;
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
|
// _stoppedMessageId: User hat diese Antwort mid-Wiedergabe gestoppt
|
||||||
|
// (Mute geklickt). Auch wenn Mute jetzt wieder aus ist, soll diese
|
||||||
|
// Antwort nicht weiterspielen. Erst eine neue messageId resetted das.
|
||||||
|
const incomingMsgId = payload.messageId || '';
|
||||||
|
const stoppedByUser = !!this._stoppedMessageId && incomingMsgId === this._stoppedMessageId;
|
||||||
// Globaler Mute-Flag uebersteuert das per-Call silent — verhindert
|
// Globaler Mute-Flag uebersteuert das per-Call silent — verhindert
|
||||||
// Race-Conditions wenn der User zwischen Chunks den Mute-Knopf drueckt.
|
// Race-Conditions wenn der User zwischen Chunks den Mute-Knopf drueckt.
|
||||||
// _pausedForCall: AudioTrack ist gestoppt waehrend Anruf — Chunks weiter
|
// _pausedForCall: AudioTrack ist gestoppt waehrend Anruf — Chunks weiter
|
||||||
// sammeln (fuer WAV-Cache), aber NICHT in den Player schicken.
|
// sammeln (fuer WAV-Cache), aber NICHT in den Player schicken.
|
||||||
const silent = !!payload.silent || this._muted || this._pausedForCall;
|
const silent = !!payload.silent || this._muted || this._pausedForCall || stoppedByUser;
|
||||||
if (!silent && !PcmStreamPlayer) {
|
if (!silent && !PcmStreamPlayer) {
|
||||||
console.warn('[Audio] PcmStreamPlayer Native Module nicht verfuegbar');
|
console.warn('[Audio] PcmStreamPlayer Native Module nicht verfuegbar');
|
||||||
return '';
|
return '';
|
||||||
@@ -913,6 +918,13 @@ class AudioService {
|
|||||||
this.pausedMessageId = '';
|
this.pausedMessageId = '';
|
||||||
this.pausedPosition = 0;
|
this.pausedPosition = 0;
|
||||||
}
|
}
|
||||||
|
// Stop-Marker zuruecksetzen wenn neue messageId — neue Antwort darf
|
||||||
|
// wieder normal abspielen, egal ob Mute zwischendurch aktiv war.
|
||||||
|
if (this._stoppedMessageId && this._stoppedMessageId !== messageId) {
|
||||||
|
console.log('[Audio] Neue Antwort (msgId=%s) — Stop-Marker fuer %s zurueckgesetzt',
|
||||||
|
messageId, this._stoppedMessageId);
|
||||||
|
this._stoppedMessageId = '';
|
||||||
|
}
|
||||||
this.pcmStreamActive = true;
|
this.pcmStreamActive = true;
|
||||||
this.pcmMessageId = messageId;
|
this.pcmMessageId = messageId;
|
||||||
this.pcmSampleRate = sampleRate;
|
this.pcmSampleRate = sampleRate;
|
||||||
@@ -1181,11 +1193,26 @@ class AudioService {
|
|||||||
* abgespielt. Wird in pauseForCall gesetzt, in endCallPause/resumeFrom-
|
* abgespielt. Wird in pauseForCall gesetzt, in endCallPause/resumeFrom-
|
||||||
* Interruption zurueckgenommen. */
|
* Interruption zurueckgenommen. */
|
||||||
private _pausedForCall: boolean = false;
|
private _pausedForCall: boolean = false;
|
||||||
|
/** Wenn der User mid-Wiedergabe Mute drueckt: messageId der ABGEBROCHENEN
|
||||||
|
* Antwort merken. Folge-Chunks dieser msgId werden silent ignoriert, auch
|
||||||
|
* wenn der User Mute wieder ausschaltet — kein "Resume mid-Antwort". Eine
|
||||||
|
* NEUE messageId resetted das, dann spielt's wieder normal. */
|
||||||
|
private _stoppedMessageId: string = '';
|
||||||
setMuted(muted: boolean): void {
|
setMuted(muted: boolean): void {
|
||||||
console.log('[Audio] setMuted: %s (currentSound=%s pcmStreamActive=%s)',
|
console.log('[Audio] setMuted: %s (currentSound=%s pcmStreamActive=%s)',
|
||||||
muted, this.currentSound ? 'aktiv' : 'null', this.pcmStreamActive);
|
muted, this.currentSound ? 'aktiv' : 'null', this.pcmStreamActive);
|
||||||
this._muted = muted;
|
this._muted = muted;
|
||||||
if (muted) this.stopPlayback();
|
if (muted) {
|
||||||
|
// Aktuell laufende Antwort als "verworfen" markieren — nachfolgende
|
||||||
|
// chunks dieser msgId werden silent gehalten auch wenn der User Mute
|
||||||
|
// gleich wieder ausschaltet. Erst eine NEUE Antwort darf wieder reden.
|
||||||
|
const activeMsgId = this.pcmMessageId || this.currentPlaybackMsgId;
|
||||||
|
if (activeMsgId) {
|
||||||
|
this._stoppedMessageId = activeMsgId;
|
||||||
|
console.log('[Audio] Antwort %s als gestoppt markiert', activeMsgId);
|
||||||
|
}
|
||||||
|
this.stopPlayback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
isMuted(): boolean { return this._muted; }
|
isMuted(): boolean { return this._muted; }
|
||||||
|
|
||||||
@@ -1204,10 +1231,6 @@ class AudioService {
|
|||||||
stopBackgroundAudio().catch(() => {});
|
stopBackgroundAudio().catch(() => {});
|
||||||
this.audioQueue = [];
|
this.audioQueue = [];
|
||||||
this.isPlaying = false;
|
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) {
|
if (this.currentSound) {
|
||||||
this.currentSound.stop();
|
this.currentSound.stop();
|
||||||
this.currentSound.release();
|
this.currentSound.release();
|
||||||
@@ -1233,14 +1256,13 @@ class AudioService {
|
|||||||
this.pcmBuffer = [];
|
this.pcmBuffer = [];
|
||||||
this.pcmBytesCollected = 0;
|
this.pcmBytesCollected = 0;
|
||||||
this.pcmMessageId = '';
|
this.pcmMessageId = '';
|
||||||
// Audio-Focus sofort freigeben — User hat explizit abgebrochen
|
// Audio-Focus sofort freigeben — User hat explizit abgebrochen.
|
||||||
|
// Unser Focus war TRANSIENT, Spotify resumed darum automatisch beim
|
||||||
|
// Abandon. Den frueheren kickReleaseMedia haben wir entfernt: er
|
||||||
|
// requestete USAGE_MEDIA mit GAIN (permanent), was Spotify als
|
||||||
|
// "user-action stopp" interpretierte und Auto-Resume verhinderte.
|
||||||
this._cancelDeferredFocusRelease();
|
this._cancelDeferredFocusRelease();
|
||||||
AudioFocus?.release().catch(() => {});
|
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 ---
|
// --- Status & Callbacks ---
|
||||||
|
|||||||
Reference in New Issue
Block a user