More audio focus changes.

This commit is contained in:
Koen J 2024-09-03 12:59:06 +02:00
parent 3cf4a52a69
commit 249e77a5d3

View File

@ -55,11 +55,15 @@ class MediaPlaybackService : Service() {
private var _notificationChannel: NotificationChannel? = null; private var _notificationChannel: NotificationChannel? = null;
private var _mediaSession: MediaSessionCompat? = null; private var _mediaSession: MediaSessionCompat? = null;
private var _hasFocus: Boolean = false; private var _hasFocus: Boolean = false;
private var _isTransientLoss: Boolean = false;
private var _focusRequest: AudioFocusRequest? = null; private var _focusRequest: AudioFocusRequest? = null;
private var _audioFocusLossTime_ms: Long? = null private var _audioFocusLossTime_ms: Long? = null
private var _playbackState = PlaybackStateCompat.STATE_NONE; private var _playbackState = PlaybackStateCompat.STATE_NONE;
private val _handler = Handler(Looper.getMainLooper()) private var _lastAudioFocusAttempt_ms: Long? = null
private val _audioFocusRunnable = Runnable { setAudioFocus(false) } private val isPlaying get() = _playbackState != PlaybackStateCompat.STATE_PAUSED &&
_playbackState != PlaybackStateCompat.STATE_STOPPED &&
_playbackState != PlaybackStateCompat.STATE_NONE &&
_playbackState != PlaybackStateCompat.STATE_ERROR
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Logger.v(TAG, "onStartCommand"); Logger.v(TAG, "onStartCommand");
@ -161,14 +165,7 @@ class MediaPlaybackService : Service() {
Logger.v(TAG, "closeMediaSession"); Logger.v(TAG, "closeMediaSession");
stopForeground(STOP_FOREGROUND_REMOVE); stopForeground(STOP_FOREGROUND_REMOVE);
val focusRequest = _focusRequest; abandonAudioFocus()
if (focusRequest != null) {
Logger.i(TAG, "Audio focus abandoned")
_handler.removeCallbacks(_audioFocusRunnable)
_audioManager?.abandonAudioFocusRequest(focusRequest);
_focusRequest = null;
}
_hasFocus = false;
val notifManager = _notificationManager; val notifManager = _notificationManager;
Logger.i(TAG, "Cancelling playback notification (notifManager: ${notifManager != null})"); Logger.i(TAG, "Cancelling playback notification (notifManager: ${notifManager != null})");
@ -339,27 +336,34 @@ class MediaPlaybackService : Service() {
.setState(state, pos, 1f, SystemClock.elapsedRealtime()) .setState(state, pos, 1f, SystemClock.elapsedRealtime())
.build()); .build());
if(_focusRequest == null)
setAudioFocus();
_playbackState = state; _playbackState = state;
try {
setAudioFocus()
} catch (e: Throwable) {
Logger.e(TAG, "Failed to set audio focus", e)
}
} }
//TODO: (TBD) This code probably more fitting inside FutoVideoPlayer, as this service is generally only used for global events //TODO: (TBD) This code probably more fitting inside FutoVideoPlayer, as this service is generally only used for global events
private fun setAudioFocus(createFocusRequest: Boolean = true) { private fun setAudioFocus() {
_handler.removeCallbacks(_audioFocusRunnable) if (!isPlaying) {
return
}
if (_hasFocus) { if (_hasFocus || _isTransientLoss) {
Log.i(TAG, "Skipped trying to get audio focus because audio focus is already obtained.");
return; return;
} }
if (_focusRequest == null) { val now = System.currentTimeMillis()
if (!createFocusRequest) { val lastAudioFocusAttempt_ms = _lastAudioFocusAttempt_ms
Log.i(TAG, "Skipped trying to get audio focus because createFocusRequest = false and no focus request exists."); if (lastAudioFocusAttempt_ms == null || now - lastAudioFocusAttempt_ms > 1000) {
return; _lastAudioFocusAttempt_ms = now
} } else {
Log.v(TAG, "Skipped trying to get audio focus because gaining audio focus was recently attempted.");
return
}
if (_focusRequest == null) {
val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAcceptsDelayedFocusGain(true) .setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(_audioFocusChangeListener) .setOnAudioFocusChangeListener(_audioFocusChangeListener)
@ -374,27 +378,39 @@ class MediaPlaybackService : Service() {
val result = _audioManager?.requestAudioFocus(_focusRequest!!) val result = _audioManager?.requestAudioFocus(_focusRequest!!)
Log.i(TAG, "Audio focus request result $result"); Log.i(TAG, "Audio focus request result $result");
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
_hasFocus = true; _hasFocus = true
Log.i(TAG, "Audio focus received"); Log.i(TAG, "Audio focus received");
} else if (result == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) { } else if (result == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
_hasFocus = false _hasFocus = false
_isTransientLoss = false
Log.i(TAG, "Audio focus delayed, waiting for focus") Log.i(TAG, "Audio focus delayed, waiting for focus")
} else { } else {
_hasFocus = false _hasFocus = false
Log.i(TAG, "Audio focus not granted, retrying in 1 second") _isTransientLoss = false
_handler.postDelayed(_audioFocusRunnable, 1000) Log.i(TAG, "Audio focus not granted, retrying later")
} }
Log.i(TAG, "Audio focus requested."); Log.i(TAG, "Audio focus requested.");
} }
private fun abandonAudioFocus() {
val focusRequest = _focusRequest;
if (focusRequest != null) {
Logger.i(TAG, "Audio focus abandoned")
_audioManager?.abandonAudioFocusRequest(focusRequest);
_focusRequest = null;
}
_hasFocus = false;
_isTransientLoss = false;
}
private val _audioFocusChangeListener = private val _audioFocusChangeListener =
OnAudioFocusChangeListener { focusChange -> OnAudioFocusChangeListener { focusChange ->
try { try {
when (focusChange) { when (focusChange) {
AudioManager.AUDIOFOCUS_GAIN -> { AudioManager.AUDIOFOCUS_GAIN -> {
_handler.removeCallbacks(_audioFocusRunnable)
_hasFocus = true; _hasFocus = true;
_isTransientLoss = false;
Log.i(TAG, "Audio focus gained (restartPlaybackAfterLoss = ${Settings.instance.playback.restartPlaybackAfterLoss}, _audioFocusLossTime_ms = $_audioFocusLossTime_ms)"); Log.i(TAG, "Audio focus gained (restartPlaybackAfterLoss = ${Settings.instance.playback.restartPlaybackAfterLoss}, _audioFocusLossTime_ms = $_audioFocusLossTime_ms)");
if (Settings.instance.playback.restartPlaybackAfterLoss == 1) { if (Settings.instance.playback.restartPlaybackAfterLoss == 1) {
@ -412,31 +428,27 @@ class MediaPlaybackService : Service() {
} }
} }
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> { AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
if (_playbackState != PlaybackStateCompat.STATE_PAUSED && if (isPlaying) {
_playbackState != PlaybackStateCompat.STATE_STOPPED &&
_playbackState != PlaybackStateCompat.STATE_NONE &&
_playbackState != PlaybackStateCompat.STATE_ERROR) {
_audioFocusLossTime_ms = System.currentTimeMillis() _audioFocusLossTime_ms = System.currentTimeMillis()
} }
_hasFocus = false; _hasFocus = false;
_isTransientLoss = true;
MediaControlReceiver.onPauseReceived.emit(); MediaControlReceiver.onPauseReceived.emit();
Log.i(TAG, "Audio focus transient loss (_audioFocusLossTime_ms = ${_audioFocusLossTime_ms})"); Log.i(TAG, "Audio focus transient loss (_audioFocusLossTime_ms = ${_audioFocusLossTime_ms})");
} }
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
Log.i(TAG, "Audio focus transient loss, can duck"); Log.i(TAG, "Audio focus transient loss, can duck");
_hasFocus = true; _hasFocus = true;
_isTransientLoss = true;
} }
AudioManager.AUDIOFOCUS_LOSS -> { AudioManager.AUDIOFOCUS_LOSS -> {
if (_playbackState != PlaybackStateCompat.STATE_PAUSED && if (isPlaying) {
_playbackState != PlaybackStateCompat.STATE_STOPPED &&
_playbackState != PlaybackStateCompat.STATE_NONE &&
_playbackState != PlaybackStateCompat.STATE_ERROR) {
_audioFocusLossTime_ms = System.currentTimeMillis() _audioFocusLossTime_ms = System.currentTimeMillis()
} }
_hasFocus = false;
MediaControlReceiver.onPauseReceived.emit(); MediaControlReceiver.onPauseReceived.emit();
abandonAudioFocus();
Log.i(TAG, "Audio focus lost"); Log.i(TAG, "Audio focus lost");
} }
} }