Skip to content

Commit 3c58b34

Browse files
eubnaraTobiGr
authored andcommitted
Fix audio ducking issue by using Android's automatic audio focus management
Previously, manual audio focus handling caused volume to not be restored after transient ducking on some devices. This change removes manual AudioFocusRequest management and relies on ExoPlayer's built-in audio focus handling (handleAudioFocus=true), which properly manages automatic ducking as recommended in: https://developer.android.com/media/optimize/audio-focus#automatic-ducking Fixes #9710
1 parent 35b70c5 commit 3c58b34

File tree

2 files changed

+10
-108
lines changed

2 files changed

+10
-108
lines changed

app/src/main/java/org/schabi/newpipe/player/Player.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,15 @@ private void initPlayer(final boolean playOnReady) {
641641
simpleExoPlayer.setWakeMode(C.WAKE_MODE_NETWORK);
642642
simpleExoPlayer.setHandleAudioBecomingNoisy(true);
643643

644+
// Enable automatic audio focus management - let Android handle ducking automatically
645+
simpleExoPlayer.setAudioAttributes(
646+
new com.google.android.exoplayer2.audio.AudioAttributes.Builder()
647+
.setUsage(C.USAGE_MEDIA)
648+
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
649+
.build(),
650+
true // handleAudioFocus = true for automatic management
651+
);
652+
644653
audioReactor = new AudioReactor(context, simpleExoPlayer);
645654

646655
registerBroadcastReceiver();
@@ -1190,10 +1199,6 @@ private void onPrepared(final boolean playWhenReady) {
11901199
}
11911200

11921201
UIs.call(PlayerUi::onPrepared);
1193-
1194-
if (playWhenReady && !isMuted()) {
1195-
audioReactor.requestAudioFocus();
1196-
}
11971202
}
11981203

11991204
private void onBlocked() {
@@ -1341,11 +1346,6 @@ public void toggleShuffleModeEnabled() {
13411346
public void toggleMute() {
13421347
final boolean wasMuted = isMuted();
13431348
simpleExoPlayer.setVolume(wasMuted ? 1 : 0);
1344-
if (wasMuted) {
1345-
audioReactor.requestAudioFocus();
1346-
} else {
1347-
audioReactor.abandonAudioFocus();
1348-
}
13491349
UIs.call(playerUi -> playerUi.onMuteUnmuteChanged(!wasMuted));
13501350
notifyPlaybackUpdateToListeners();
13511351
}
@@ -1757,10 +1757,6 @@ public void play() {
17571757
return;
17581758
}
17591759

1760-
if (!isMuted()) {
1761-
audioReactor.requestAudioFocus();
1762-
}
1763-
17641760
if (currentState == STATE_COMPLETED) {
17651761
if (playQueue.getIndex() == 0) {
17661762
seekToDefault();
@@ -1781,7 +1777,6 @@ public void pause() {
17811777
return;
17821778
}
17831779

1784-
audioReactor.abandonAudioFocus();
17851780
simpleExoPlayer.pause();
17861781
saveStreamProgressState();
17871782
}

app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java

Lines changed: 1 addition & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,36 @@
11
package org.schabi.newpipe.player.helper;
22

3-
import android.animation.Animator;
4-
import android.animation.AnimatorListenerAdapter;
5-
import android.animation.ValueAnimator;
63
import android.content.Context;
74
import android.content.Intent;
85
import android.media.AudioManager;
96
import android.media.audiofx.AudioEffect;
10-
import android.util.Log;
117

128
import androidx.annotation.NonNull;
139
import androidx.core.content.ContextCompat;
14-
import androidx.media.AudioFocusRequestCompat;
1510
import androidx.media.AudioManagerCompat;
1611

1712
import com.google.android.exoplayer2.ExoPlayer;
1813
import com.google.android.exoplayer2.analytics.AnalyticsListener;
1914

20-
public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, AnalyticsListener {
15+
public class AudioReactor implements AnalyticsListener {
2116

2217
private static final String TAG = "AudioFocusReactor";
2318

24-
private static final int DUCK_DURATION = 1500;
25-
private static final float DUCK_AUDIO_TO = .2f;
26-
27-
private static final int FOCUS_GAIN_TYPE = AudioManagerCompat.AUDIOFOCUS_GAIN;
2819
private static final int STREAM_TYPE = AudioManager.STREAM_MUSIC;
2920

3021
private final ExoPlayer player;
3122
private final Context context;
3223
private final AudioManager audioManager;
3324

34-
private final AudioFocusRequestCompat request;
35-
3625
public AudioReactor(@NonNull final Context context,
3726
@NonNull final ExoPlayer player) {
3827
this.player = player;
3928
this.context = context;
4029
this.audioManager = ContextCompat.getSystemService(context, AudioManager.class);
4130
player.addAnalyticsListener(this);
42-
43-
request = new AudioFocusRequestCompat.Builder(FOCUS_GAIN_TYPE)
44-
//.setAcceptsDelayedFocusGain(true)
45-
.setWillPauseWhenDucked(true)
46-
.setOnAudioFocusChangeListener(this)
47-
.build();
4831
}
4932

5033
public void dispose() {
51-
abandonAudioFocus();
5234
player.removeAnalyticsListener(this);
5335
notifyAudioSessionUpdate(false, player.getAudioSessionId());
5436
}
@@ -57,14 +39,6 @@ public void dispose() {
5739
// Audio Manager
5840
//////////////////////////////////////////////////////////////////////////*/
5941

60-
public void requestAudioFocus() {
61-
AudioManagerCompat.requestAudioFocus(audioManager, request);
62-
}
63-
64-
public void abandonAudioFocus() {
65-
AudioManagerCompat.abandonAudioFocusRequest(audioManager, request);
66-
}
67-
6842
public int getVolume() {
6943
return audioManager.getStreamVolume(STREAM_TYPE);
7044
}
@@ -77,73 +51,6 @@ public int getMaxVolume() {
7751
return AudioManagerCompat.getStreamMaxVolume(audioManager, STREAM_TYPE);
7852
}
7953

80-
/*//////////////////////////////////////////////////////////////////////////
81-
// AudioFocus
82-
//////////////////////////////////////////////////////////////////////////*/
83-
84-
@Override
85-
public void onAudioFocusChange(final int focusChange) {
86-
Log.d(TAG, "onAudioFocusChange() called with: focusChange = [" + focusChange + "]");
87-
switch (focusChange) {
88-
case AudioManager.AUDIOFOCUS_GAIN:
89-
onAudioFocusGain();
90-
break;
91-
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
92-
onAudioFocusLossCanDuck();
93-
break;
94-
case AudioManager.AUDIOFOCUS_LOSS:
95-
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
96-
onAudioFocusLoss();
97-
break;
98-
}
99-
}
100-
101-
private void onAudioFocusGain() {
102-
Log.d(TAG, "onAudioFocusGain() called");
103-
player.setVolume(DUCK_AUDIO_TO);
104-
animateAudio(DUCK_AUDIO_TO, 1.0f);
105-
106-
if (PlayerHelper.isResumeAfterAudioFocusGain(context)) {
107-
player.play();
108-
}
109-
}
110-
111-
private void onAudioFocusLoss() {
112-
Log.d(TAG, "onAudioFocusLoss() called");
113-
player.pause();
114-
}
115-
116-
private void onAudioFocusLossCanDuck() {
117-
Log.d(TAG, "onAudioFocusLossCanDuck() called");
118-
// Set the volume to 1/10 on ducking
119-
player.setVolume(DUCK_AUDIO_TO);
120-
}
121-
122-
private void animateAudio(final float from, final float to) {
123-
final ValueAnimator valueAnimator = new ValueAnimator();
124-
valueAnimator.setFloatValues(from, to);
125-
valueAnimator.setDuration(AudioReactor.DUCK_DURATION);
126-
valueAnimator.addListener(new AnimatorListenerAdapter() {
127-
@Override
128-
public void onAnimationStart(final Animator animation) {
129-
player.setVolume(from);
130-
}
131-
132-
@Override
133-
public void onAnimationCancel(final Animator animation) {
134-
player.setVolume(to);
135-
}
136-
137-
@Override
138-
public void onAnimationEnd(final Animator animation) {
139-
player.setVolume(to);
140-
}
141-
});
142-
valueAnimator.addUpdateListener(animation ->
143-
player.setVolume(((float) animation.getAnimatedValue())));
144-
valueAnimator.start();
145-
}
146-
14754
/*//////////////////////////////////////////////////////////////////////////
14855
// Audio Processing
14956
//////////////////////////////////////////////////////////////////////////*/

0 commit comments

Comments
 (0)