Merge pull request #156 from jakobkukla/refactor-audio-focus

Refactor: Let ExoPlayer handle audio focus
This commit is contained in:
dkanada 2021-06-08 00:35:13 +09:00 committed by GitHub
commit f014bcbfdc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 21 additions and 220 deletions

View file

@ -107,9 +107,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
public static final int PLAY_SONG = 3;
public static final int PREPARE_NEXT = 4;
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 SHUFFLE_MODE_NONE = 0;
public static final int SHUFFLE_MODE_SHUFFLE = 1;
@ -142,10 +139,8 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
private boolean notHandledMetaChangedForCurrentTrack;
private boolean queuesRestored;
private boolean pausedByTransientLossOfFocus;
private PlayingNotification playingNotification;
private AudioManager audioManager;
private MediaSessionCompat mediaSession;
private PowerManager.WakeLock wakeLock;
@ -188,13 +183,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
}
};
private final AudioManager.OnAudioFocusChangeListener audioFocusListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(final int focusChange) {
playerHandler.obtainMessage(FOCUS_CHANGE, focusChange, 0).sendToTarget();
}
};
private static final long MEDIA_SESSION_ACTIONS = PlaybackStateCompat.ACTION_PLAY
| PlaybackStateCompat.ACTION_PAUSE
| PlaybackStateCompat.ACTION_PLAY_PAUSE
@ -241,14 +229,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
mediaSession.setActive(true);
}
private AudioManager getAudioManager() {
if (audioManager == null) {
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
return audioManager;
}
private void initMediaSession() {
ComponentName mediaButtonReceiverComponentName = new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class);
@ -458,7 +438,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
pause();
playingNotification.stop();
getAudioManager().abandonAudioFocus(audioFocusListener);
stopSelf();
}
@ -526,10 +505,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
}
}
private boolean requestFocus() {
return getAudioManager().requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
public void initNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !PreferenceUtil.getInstance(this).getClassicNotification()) {
playingNotification = new PlayingNotificationNougat();
@ -805,7 +780,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
}
public void pause() {
pausedByTransientLossOfFocus = false;
if (playback.isPlaying()) {
playback.pause();
notifyChange(STATE_CHANGED);
@ -814,27 +788,18 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
public void play() {
synchronized (this) {
if (requestFocus()) {
if (!playback.isPlaying()) {
if (!playback.isReady()) {
playSongAt(getPosition());
} else {
playback.start();
if (notHandledMetaChangedForCurrentTrack) {
handleChangeInternal(META_CHANGED);
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);
if (!playback.isPlaying()) {
if (!playback.isReady()) {
playSongAt(getPosition());
} else {
playback.start();
if (notHandledMetaChangedForCurrentTrack) {
handleChangeInternal(META_CHANGED);
notHandledMetaChangedForCurrentTrack = false;
}
notifyChange(STATE_CHANGED);
}
} else {
Toast.makeText(this, getResources().getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT).show();
}
}
}
@ -1065,7 +1030,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
private static final class PlaybackHandler extends Handler {
private final WeakReference<MusicService> mService;
private int currentDuckVolume = 100;
public PlaybackHandler(final MusicService service, @NonNull final Looper looper) {
super(looper);
@ -1080,36 +1044,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
}
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:
if (service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) {
service.pause();
@ -1157,40 +1091,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
case PREPARE_NEXT:
service.prepareNextImpl();
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;
}
}
}

View file

@ -9,6 +9,8 @@ import com.dkanada.gramophone.R;
import com.dkanada.gramophone.model.Song;
import com.dkanada.gramophone.util.MusicUtil;
import com.dkanada.gramophone.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.Player;
import com.google.android.exoplayer2.Player.EventListener;
@ -82,7 +84,15 @@ public class LocalPlayer implements Playback {
this.context = context;
MediaSourceFactory mediaSourceFactory = new UnknownMediaSourceFactory(buildDataSourceFactory());
exoPlayer = new SimpleExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build();
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setUsage(C.USAGE_MEDIA)
.setContentType(C.CONTENT_TYPE_MUSIC)
.build();
exoPlayer = new SimpleExoPlayer.Builder(context)
.setMediaSourceFactory(mediaSourceFactory)
.setAudioAttributes(audioAttributes, true)
.build();
exoPlayer.addListener(eventListener);
exoPlayer.prepare();

View file

@ -79,7 +79,6 @@ public final class PreferenceUtil {
public static final String TRANSCODE_CODEC = "transcode_codec";
public static final String DIRECT_PLAY_CODECS = "direct_play_codecs";
public static final String MAXIMUM_BITRATE = "maximum_bitrate";
public static final String AUDIO_DUCKING = "audio_ducking";
public static final String REMEMBER_SHUFFLE = "remember_shuffle";
public static final String REMEMBER_QUEUE = "remember_queue";
@ -247,10 +246,6 @@ public final class PreferenceUtil {
return mPreferences.getString(MAXIMUM_BITRATE, "10000000");
}
public final boolean getAudioDucking() {
return mPreferences.getBoolean(AUDIO_DUCKING, true);
}
public final boolean getRememberShuffle() {
return mPreferences.getBoolean(REMEMBER_SHUFFLE, true);
}