Skip to content

Commit af995e2

Browse files
committed
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 739b6ae commit af995e2

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
@@ -645,6 +645,15 @@ private void initPlayer(final boolean playOnReady) {
645645
simpleExoPlayer.setWakeMode(C.WAKE_MODE_NETWORK);
646646
simpleExoPlayer.setHandleAudioBecomingNoisy(true);
647647

648+
// Enable automatic audio focus management - let Android handle ducking automatically
649+
simpleExoPlayer.setAudioAttributes(
650+
new com.google.android.exoplayer2.audio.AudioAttributes.Builder()
651+
.setUsage(C.USAGE_MEDIA)
652+
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
653+
.build(),
654+
true // handleAudioFocus = true for automatic management
655+
);
656+
648657
audioReactor = new AudioReactor(context, simpleExoPlayer);
649658

650659
registerBroadcastReceiver();
@@ -1194,10 +1203,6 @@ private void onPrepared(final boolean playWhenReady) {
11941203
}
11951204

11961205
UIs.call(PlayerUi::onPrepared);
1197-
1198-
if (playWhenReady && !isMuted()) {
1199-
audioReactor.requestAudioFocus();
1200-
}
12011206
}
12021207

12031208
private void onBlocked() {
@@ -1345,11 +1350,6 @@ public void toggleShuffleModeEnabled() {
13451350
public void toggleMute() {
13461351
final boolean wasMuted = isMuted();
13471352
simpleExoPlayer.setVolume(wasMuted ? 1 : 0);
1348-
if (wasMuted) {
1349-
audioReactor.requestAudioFocus();
1350-
} else {
1351-
audioReactor.abandonAudioFocus();
1352-
}
13531353
UIs.call(playerUi -> playerUi.onMuteUnmuteChanged(!wasMuted));
13541354
notifyPlaybackUpdateToListeners();
13551355
}
@@ -1761,10 +1761,6 @@ public void play() {
17611761
return;
17621762
}
17631763

1764-
if (!isMuted()) {
1765-
audioReactor.requestAudioFocus();
1766-
}
1767-
17681764
if (currentState == STATE_COMPLETED) {
17691765
if (playQueue.getIndex() == 0) {
17701766
seekToDefault();
@@ -1785,7 +1781,6 @@ public void pause() {
17851781
return;
17861782
}
17871783

1788-
audioReactor.abandonAudioFocus();
17891784
simpleExoPlayer.pause();
17901785
saveStreamProgressState();
17911786
}

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)