Fixed audio focus handling

This commit is contained in:
Nite 2020-07-07 18:50:26 +02:00
parent aae40c076e
commit 3d61dde83f
No known key found for this signature in database
GPG Key ID: 1D1AD59B1C6386C1
4 changed files with 99 additions and 63 deletions

View File

@ -0,0 +1,90 @@
package org.moire.ultrasonic.service;
import android.content.Context;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.util.Log;
import org.moire.ultrasonic.domain.PlayerState;
import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.Util;
import kotlin.Lazy;
import static org.koin.java.standalone.KoinJavaComponent.inject;
public class AudioFocusHandler
{
private static final String TAG = AudioFocusHandler.class.getSimpleName();
private static boolean hasFocus;
private static boolean pauseFocus;
private static boolean lowerFocus;
// TODO: This is a circular reference, try to remove it
private Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
private Context context;
public AudioFocusHandler(Context context)
{
this.context = context;
}
public void requestAudioFocus()
{
if (!hasFocus)
{
final AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
hasFocus = true;
audioManager.requestAudioFocus(new AudioManager.OnAudioFocusChangeListener()
{
@Override
public void onAudioFocusChange(int focusChange)
{
MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue();
if ((focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) && !mediaPlayerController.isJukeboxEnabled())
{
Log.v(TAG, "Lost Audio Focus");
if (mediaPlayerController.getPlayerState() == PlayerState.STARTED)
{
SharedPreferences preferences = Util.getPreferences(context);
int lossPref = Integer.parseInt(preferences.getString(Constants.PREFERENCES_KEY_TEMP_LOSS, "1"));
if (lossPref == 2 || (lossPref == 1 && focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK))
{
lowerFocus = true;
mediaPlayerController.setVolume(0.1f);
}
else if (lossPref == 0 || (lossPref == 1))
{
pauseFocus = true;
mediaPlayerController.pause();
}
}
}
else if (focusChange == AudioManager.AUDIOFOCUS_GAIN)
{
Log.v(TAG, "Regained Audio Focus");
if (pauseFocus)
{
pauseFocus = false;
mediaPlayerController.start();
}
else if (lowerFocus)
{
lowerFocus = false;
mediaPlayerController.setVolume(1.0f);
}
}
else if (focusChange == AudioManager.AUDIOFOCUS_LOSS && !mediaPlayerController.isJukeboxEnabled())
{
hasFocus = false;
mediaPlayerController.pause();
audioManager.abandonAudioFocus(this);
Log.v(TAG, "Abandoned Audio Focus");
}
}
}, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
Log.v(TAG, "Got Audio Focus");
}
}
}

View File

@ -84,6 +84,7 @@ public class LocalMediaPlayer
private PositionCache positionCache; private PositionCache positionCache;
private int secondaryProgress = -1; private int secondaryProgress = -1;
private final AudioFocusHandler audioFocusHandler;
private final Context context; private final Context context;
static static
@ -112,8 +113,9 @@ public class LocalMediaPlayer
} }
} }
public LocalMediaPlayer(Context context) public LocalMediaPlayer(AudioFocusHandler audioFocusHandler, Context context)
{ {
this.audioFocusHandler = audioFocusHandler;
this.context = context; this.context = context;
} }
@ -240,9 +242,9 @@ public class LocalMediaPlayer
Util.unregisterMediaButtonEventReceiver(context, true); Util.unregisterMediaButtonEventReceiver(context, true);
wakeLock.release(); wakeLock.release();
} }
catch (Throwable ignored) catch (Throwable exception)
{ {
Log.w(TAG, "LocalMediaPlayer onDestroy exception: ", ignored); Log.w(TAG, "LocalMediaPlayer onDestroy exception: ", exception);
} }
Log.i(TAG, "LocalMediaPlayer destroyed"); Log.i(TAG, "LocalMediaPlayer destroyed");
@ -286,7 +288,7 @@ public class LocalMediaPlayer
if (playerState == PlayerState.STARTED) if (playerState == PlayerState.STARTED)
{ {
Util.requestAudioFocus(context); audioFocusHandler.requestAudioFocus();
} }
if (playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED) if (playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED)

View File

@ -92,10 +92,6 @@ public class Util extends DownloadActivity
public static final String CM_AVRCP_PLAYSTATE_CHANGED = "com.android.music.playstatechanged"; public static final String CM_AVRCP_PLAYSTATE_CHANGED = "com.android.music.playstatechanged";
public static final String CM_AVRCP_METADATA_CHANGED = "com.android.music.metachanged"; public static final String CM_AVRCP_METADATA_CHANGED = "com.android.music.metachanged";
private static boolean hasFocus;
private static boolean pauseFocus;
private static boolean lowerFocus;
private static boolean mediaButtonsRegisteredForUI; private static boolean mediaButtonsRegisteredForUI;
private static boolean mediaButtonsRegisteredForService; private static boolean mediaButtonsRegisteredForService;
@ -1181,60 +1177,6 @@ public class Util extends DownloadActivity
return size; return size;
} }
public static void requestAudioFocus(final Context context)
{
if (!hasFocus)
{
final AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
hasFocus = true;
audioManager.requestAudioFocus(new OnAudioFocusChangeListener()
{
@Override
public void onAudioFocusChange(int focusChange)
{
MediaPlayerController mediaPlayerController = (MediaPlayerController) context;
if ((focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) && !mediaPlayerController.isJukeboxEnabled())
{
if (mediaPlayerController.getPlayerState() == PlayerState.STARTED)
{
SharedPreferences preferences = getPreferences(context);
int lossPref = Integer.parseInt(preferences.getString(Constants.PREFERENCES_KEY_TEMP_LOSS, "1"));
if (lossPref == 2 || (lossPref == 1 && focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK))
{
lowerFocus = true;
mediaPlayerController.setVolume(0.1f);
}
else if (lossPref == 0 || (lossPref == 1))
{
pauseFocus = true;
mediaPlayerController.pause();
}
}
}
else if (focusChange == AudioManager.AUDIOFOCUS_GAIN)
{
if (pauseFocus)
{
pauseFocus = false;
mediaPlayerController.start();
}
else if (lowerFocus)
{
lowerFocus = false;
mediaPlayerController.setVolume(1.0f);
}
}
else if (focusChange == AudioManager.AUDIOFOCUS_LOSS && !mediaPlayerController.isJukeboxEnabled())
{
hasFocus = false;
mediaPlayerController.pause();
audioManager.abandonAudioFocus(this);
}
}
}, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
}
}
public static int getMinDisplayMetric(Context context) public static int getMinDisplayMetric(Context context)
{ {
DisplayMetrics metrics = context.getResources().getDisplayMetrics(); DisplayMetrics metrics = context.getResources().getDisplayMetrics();

View File

@ -10,6 +10,7 @@ import org.moire.ultrasonic.service.LocalMediaPlayer
import org.moire.ultrasonic.service.MediaPlayerController import org.moire.ultrasonic.service.MediaPlayerController
import org.moire.ultrasonic.service.MediaPlayerControllerImpl import org.moire.ultrasonic.service.MediaPlayerControllerImpl
import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport
import org.moire.ultrasonic.service.AudioFocusHandler
import org.moire.ultrasonic.util.ShufflePlayBuffer import org.moire.ultrasonic.util.ShufflePlayBuffer
val mediaPlayerModule = module { val mediaPlayerModule = module {
@ -23,7 +24,8 @@ val mediaPlayerModule = module {
single { ExternalStorageMonitor(androidContext()) } single { ExternalStorageMonitor(androidContext()) }
single { ShufflePlayBuffer(androidContext()) } single { ShufflePlayBuffer(androidContext()) }
single { Downloader(androidContext(), get(), get(), get()) } single { Downloader(androidContext(), get(), get(), get()) }
single { LocalMediaPlayer(androidContext()) } single { LocalMediaPlayer(get(), androidContext()) }
single { AudioFocusHandler(get()) }
// TODO Ideally this can be cleaned up when all circular references are removed. // TODO Ideally this can be cleaned up when all circular references are removed.
single { MediaPlayerControllerImpl(androidContext(), get(), get(), get(), get(), get()) } single { MediaPlayerControllerImpl(androidContext(), get(), get(), get(), get(), get()) }