Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions app/src/main/java/org/schabi/newpipe/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,15 @@ private void initPlayer(final boolean playOnReady) {
simpleExoPlayer.setWakeMode(C.WAKE_MODE_NETWORK);
simpleExoPlayer.setHandleAudioBecomingNoisy(true);

// Enable automatic audio focus management - let Android handle ducking automatically
simpleExoPlayer.setAudioAttributes(
new com.google.android.exoplayer2.audio.AudioAttributes.Builder()
.setUsage(C.USAGE_MEDIA)
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
.build(),
true // handleAudioFocus = true for automatic management
);

audioReactor = new AudioReactor(context, simpleExoPlayer);

registerBroadcastReceiver();
Expand Down Expand Up @@ -1190,10 +1199,6 @@ private void onPrepared(final boolean playWhenReady) {
}

UIs.call(PlayerUi::onPrepared);

if (playWhenReady && !isMuted()) {
audioReactor.requestAudioFocus();
}
}

private void onBlocked() {
Expand Down Expand Up @@ -1340,10 +1345,32 @@ public void toggleShuffleModeEnabled() {

public void toggleMute() {
final boolean wasMuted = isMuted();
final boolean wasPlaying = simpleExoPlayer.isPlaying();
Log.d(TAG, "toggleMute: wasMuted=" + wasMuted + ", wasPlaying=" + wasPlaying);
simpleExoPlayer.setVolume(wasMuted ? 1 : 0);
if (wasMuted) {
audioReactor.requestAudioFocus();
Log.d(TAG, "toggleMute: enabling audio focus, willPlay=" + wasPlaying);
simpleExoPlayer.setAudioAttributes(
new com.google.android.exoplayer2.audio.AudioAttributes.Builder()
.setUsage(C.USAGE_MEDIA)
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
.build(),
true // handleAudioFocus = true - ExoPlayer handles focus automatically
);
if (wasPlaying) {
Log.d(TAG, "toggleMute: calling play(), isPlaying=" + simpleExoPlayer.isPlaying());
simpleExoPlayer.play();
Log.d(TAG, "toggleMute: after play(), isPlaying=" + simpleExoPlayer.isPlaying());
}
} else {
Log.d(TAG, "toggleMute: disabling audio focus, abandoning focus");
simpleExoPlayer.setAudioAttributes(
new com.google.android.exoplayer2.audio.AudioAttributes.Builder()
.setUsage(C.USAGE_MEDIA)
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
.build(),
false // handleAudioFocus = false
);
audioReactor.abandonAudioFocus();
}
UIs.call(playerUi -> playerUi.onMuteUnmuteChanged(!wasMuted));
Expand Down Expand Up @@ -1757,10 +1784,6 @@ public void play() {
return;
}

if (!isMuted()) {
audioReactor.requestAudioFocus();
}

if (currentState == STATE_COMPLETED) {
if (playQueue.getIndex() == 0) {
seekToDefault();
Expand All @@ -1781,7 +1804,6 @@ public void pause() {
return;
}

audioReactor.abandonAudioFocus();
simpleExoPlayer.pause();
saveStreamProgressState();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package org.schabi.newpipe.player.helper;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
Expand All @@ -17,20 +14,16 @@
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.analytics.AnalyticsListener;

public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, AnalyticsListener {
public class AudioReactor implements AnalyticsListener {

private static final String TAG = "AudioFocusReactor";

private static final int DUCK_DURATION = 1500;
private static final float DUCK_AUDIO_TO = .2f;

private static final int FOCUS_GAIN_TYPE = AudioManagerCompat.AUDIOFOCUS_GAIN;
private static final int STREAM_TYPE = AudioManager.STREAM_MUSIC;

private final ExoPlayer player;
private final Context context;
private final AudioManager audioManager;

private final AudioFocusRequestCompat request;

public AudioReactor(@NonNull final Context context,
Expand All @@ -41,9 +34,9 @@ public AudioReactor(@NonNull final Context context,
player.addAnalyticsListener(this);

request = new AudioFocusRequestCompat.Builder(FOCUS_GAIN_TYPE)
//.setAcceptsDelayedFocusGain(true)
.setWillPauseWhenDucked(true)
.setOnAudioFocusChangeListener(this)
.setOnAudioFocusChangeListener(focusChange -> {
})
.build();
}

Expand All @@ -54,17 +47,23 @@ public void dispose() {
}

/*//////////////////////////////////////////////////////////////////////////
// Audio Manager
// Audio Focus
//////////////////////////////////////////////////////////////////////////*/

public void requestAudioFocus() {
Log.d(TAG, "requestAudioFocus() called");
AudioManagerCompat.requestAudioFocus(audioManager, request);
}

public void abandonAudioFocus() {
Log.d(TAG, "abandonAudioFocus() called");
AudioManagerCompat.abandonAudioFocusRequest(audioManager, request);
}

/*//////////////////////////////////////////////////////////////////////////
// Audio Manager
//////////////////////////////////////////////////////////////////////////*/

public int getVolume() {
return audioManager.getStreamVolume(STREAM_TYPE);
}
Expand All @@ -77,73 +76,6 @@ public int getMaxVolume() {
return AudioManagerCompat.getStreamMaxVolume(audioManager, STREAM_TYPE);
}

/*//////////////////////////////////////////////////////////////////////////
// AudioFocus
//////////////////////////////////////////////////////////////////////////*/

@Override
public void onAudioFocusChange(final int focusChange) {
Log.d(TAG, "onAudioFocusChange() called with: focusChange = [" + focusChange + "]");
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
onAudioFocusGain();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
onAudioFocusLossCanDuck();
break;
case AudioManager.AUDIOFOCUS_LOSS:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
onAudioFocusLoss();
break;
}
}

private void onAudioFocusGain() {
Log.d(TAG, "onAudioFocusGain() called");
player.setVolume(DUCK_AUDIO_TO);
animateAudio(DUCK_AUDIO_TO, 1.0f);

if (PlayerHelper.isResumeAfterAudioFocusGain(context)) {
player.play();
}
}

private void onAudioFocusLoss() {
Log.d(TAG, "onAudioFocusLoss() called");
player.pause();
}

private void onAudioFocusLossCanDuck() {
Log.d(TAG, "onAudioFocusLossCanDuck() called");
// Set the volume to 1/10 on ducking
player.setVolume(DUCK_AUDIO_TO);
}

private void animateAudio(final float from, final float to) {
final ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setFloatValues(from, to);
valueAnimator.setDuration(AudioReactor.DUCK_DURATION);
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(final Animator animation) {
player.setVolume(from);
}

@Override
public void onAnimationCancel(final Animator animation) {
player.setVolume(to);
}

@Override
public void onAnimationEnd(final Animator animation) {
player.setVolume(to);
}
});
valueAnimator.addUpdateListener(animation ->
player.setVolume(((float) animation.getAnimatedValue())));
valueAnimator.start();
}

/*//////////////////////////////////////////////////////////////////////////
// Audio Processing
//////////////////////////////////////////////////////////////////////////*/
Expand Down
Loading