refactor: let exoplayer handle audio focus

commit fcf2c62d9a1d997be64c268c7689b4ca59fef1d4 by jakobkukla
This commit is contained in:
CappielloAntonio 2021-07-17 18:52:12 +02:00
parent 7e2ba5a71a
commit 3a0888ac31
2 changed files with 18 additions and 111 deletions

View file

@ -11,6 +11,8 @@ import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.util.DownloadUtil; import com.cappielloantonio.play.util.DownloadUtil;
import com.cappielloantonio.play.util.MusicUtil; import com.cappielloantonio.play.util.MusicUtil;
import com.cappielloantonio.play.util.PreferenceUtil; import com.cappielloantonio.play.util.PreferenceUtil;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
@ -88,8 +90,14 @@ public class MultiPlayer implements Playback {
.setUpstreamDataSourceFactory(DownloadUtil.getHttpDataSourceFactory(context)) .setUpstreamDataSourceFactory(DownloadUtil.getHttpDataSourceFactory(context))
.setCacheWriteDataSinkFactory(null); // Disable writing. .setCacheWriteDataSinkFactory(null); // Disable writing.
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setUsage(C.USAGE_MEDIA)
.setContentType(C.CONTENT_TYPE_MUSIC)
.build();
exoPlayer = new SimpleExoPlayer.Builder(context) exoPlayer = new SimpleExoPlayer.Builder(context)
.setMediaSourceFactory(new DefaultMediaSourceFactory(cacheDataSourceFactory)) .setMediaSourceFactory(new DefaultMediaSourceFactory(cacheDataSourceFactory))
.setAudioAttributes(audioAttributes, true)
.build(); .build();
// TODO: Player is accessed on the wrong thread suppressed // TODO: Player is accessed on the wrong thread suppressed

View file

@ -56,6 +56,7 @@ import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_
import static com.google.android.exoplayer2.Player.PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM; import static com.google.android.exoplayer2.Player.PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM;
public class MusicService extends Service implements Playback.PlaybackCallbacks { public class MusicService extends Service implements Playback.PlaybackCallbacks {
private static final String TAG = "MusicService";
public static final String PACKAGE_NAME = "com.antoniocappiello.play"; public static final String PACKAGE_NAME = "com.antoniocappiello.play";
public static final String ACTION_TOGGLE = PACKAGE_NAME + ".toggle"; public static final String ACTION_TOGGLE = PACKAGE_NAME + ".toggle";
public static final String ACTION_PLAY = PACKAGE_NAME + ".play"; public static final String ACTION_PLAY = PACKAGE_NAME + ".play";
@ -77,11 +78,7 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
public static final int PLAY_SONG = 3; public static final int PLAY_SONG = 3;
public static final int PREPARE_NEXT = 4; public static final int PREPARE_NEXT = 4;
public static final int SET_POSITION = 5; public static final int SET_POSITION = 5;
public static final int FOCUS_CHANGE = 6;
public static final int DUCK = 7;
public static final int UNDUCK = 8;
public static final int LOAD_QUEUE = 9; public static final int LOAD_QUEUE = 9;
private static final String TAG = "MusicService";
private static final long MEDIA_SESSION_ACTIONS = PlaybackStateCompat.ACTION_PLAY private static final long MEDIA_SESSION_ACTIONS = PlaybackStateCompat.ACTION_PLAY
| PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PAUSE
| PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE
@ -97,7 +94,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
private int nextPosition = -1; private int nextPosition = -1;
private boolean notHandledMetaChangedForCurrentTrack; private boolean notHandledMetaChangedForCurrentTrack;
private boolean queuesRestored; private boolean queuesRestored;
private boolean pausedByTransientLossOfFocus;
private PlayingNotification playingNotification; private PlayingNotification playingNotification;
private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() { private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() {
@Override @Override
@ -107,16 +103,9 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
} }
} }
}; };
private AudioManager audioManager;
private MediaSessionCompat mediaSession; private MediaSessionCompat mediaSession;
private PowerManager.WakeLock wakeLock; private PowerManager.WakeLock wakeLock;
private PlaybackHandler playerHandler; private PlaybackHandler playerHandler;
private final AudioManager.OnAudioFocusChangeListener audioFocusListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(final int focusChange) {
playerHandler.obtainMessage(FOCUS_CHANGE, focusChange, 0).sendToTarget();
}
};
private Handler uiThreadHandler; private Handler uiThreadHandler;
private ThrottledSeekHandler throttledSeekHandler; private ThrottledSeekHandler throttledSeekHandler;
private QueueHandler queueHandler; private QueueHandler queueHandler;
@ -160,14 +149,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
mediaSession.setActive(true); mediaSession.setActive(true);
} }
private AudioManager getAudioManager() {
if (audioManager == null) {
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
return audioManager;
}
private void initMediaSession() { private void initMediaSession() {
ComponentName mediaButtonReceiverComponentName = new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class); ComponentName mediaButtonReceiverComponentName = new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class);
@ -332,8 +313,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
private void quit() { private void quit() {
pause(); pause();
playingNotification.stop(); playingNotification.stop();
getAudioManager().abandonAudioFocus(audioFocusListener);
stopSelf(); stopSelf();
} }
@ -412,10 +391,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
increaseSongCount(); increaseSongCount();
} }
private boolean requestFocus() {
return getAudioManager().requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
public void initNotification() { public void initNotification() {
playingNotification = new PlayingNotification(); playingNotification = new PlayingNotification();
playingNotification.init(this); playingNotification.init(this);
@ -582,7 +557,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
} }
public void pause() { public void pause() {
pausedByTransientLossOfFocus = false;
if (playback.isPlaying()) { if (playback.isPlaying()) {
playback.pause(); playback.pause();
notifyChange(STATE_CHANGED); notifyChange(STATE_CHANGED);
@ -591,27 +565,17 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
public void play() { public void play() {
synchronized (this) { synchronized (this) {
if (requestFocus()) { if (!playback.isPlaying()) {
if (!playback.isPlaying()) { if (!playback.isReady()) {
if (!playback.isReady()) { playSongAt(getPosition());
playSongAt(getPosition()); } else {
} else { playback.start();
playback.start(); if (notHandledMetaChangedForCurrentTrack) {
if (notHandledMetaChangedForCurrentTrack) { handleChangeInternal(META_CHANGED);
handleChangeInternal(META_CHANGED); notHandledMetaChangedForCurrentTrack = false;
notHandledMetaChangedForCurrentTrack = false;
}
notifyChange(STATE_CHANGED);
// fixes a bug where the volume would stay ducked
// happens when audio focus GAIN event not sent
playerHandler.removeMessages(DUCK);
playerHandler.sendEmptyMessage(UNDUCK);
} }
notifyChange(STATE_CHANGED);
} }
} else {
Toast.makeText(this, getResources().getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT).show();
} }
} }
} }
@ -758,7 +722,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
private static final class PlaybackHandler extends Handler { private static final class PlaybackHandler extends Handler {
private final WeakReference<MusicService> mService; private final WeakReference<MusicService> mService;
private int currentDuckVolume = 100;
public PlaybackHandler(final MusicService service, @NonNull final Looper looper) { public PlaybackHandler(final MusicService service, @NonNull final Looper looper) {
super(looper); super(looper);
@ -773,36 +736,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
} }
switch (msg.what) { switch (msg.what) {
case DUCK:
if (PreferenceUtil.getInstance(service).getAudioDucking()) {
currentDuckVolume -= 5;
if (currentDuckVolume > 20) {
sendEmptyMessageDelayed(DUCK, 10);
} else {
currentDuckVolume = 20;
}
} else {
currentDuckVolume = 100;
}
service.playback.setVolume(currentDuckVolume);
break;
case UNDUCK:
if (PreferenceUtil.getInstance(service).getAudioDucking()) {
currentDuckVolume += 3;
if (currentDuckVolume < 100) {
sendEmptyMessageDelayed(UNDUCK, 10);
} else {
currentDuckVolume = 100;
}
} else {
currentDuckVolume = 100;
}
service.playback.setVolume(currentDuckVolume);
break;
case TRACK_CHANGED: case TRACK_CHANGED:
if (service.isLastTrack()) { if (service.isLastTrack()) {
service.pause(); service.pause();
@ -851,40 +784,6 @@ public class MusicService extends Service implements Playback.PlaybackCallbacks
case PREPARE_NEXT: case PREPARE_NEXT:
service.prepareNextImpl(); service.prepareNextImpl();
break; break;
case FOCUS_CHANGE:
switch (msg.arg1) {
case AudioManager.AUDIOFOCUS_GAIN:
if (!service.isPlaying() && service.pausedByTransientLossOfFocus) {
service.play();
service.pausedByTransientLossOfFocus = false;
}
removeMessages(DUCK);
sendEmptyMessage(UNDUCK);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media playback
service.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop
// playback. We don't release the media playback because playback
// is likely to resume
boolean wasPlaying = service.isPlaying();
service.pause();
service.pausedByTransientLossOfFocus = wasPlaying;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
removeMessages(UNDUCK);
sendEmptyMessage(DUCK);
break;
}
break;
} }
} }
} }