diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java index 506db741..524a1fbf 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java @@ -492,13 +492,8 @@ public class SettingsFragment extends PreferenceFragmentCompat } private void setMediaButtonsEnabled(boolean enabled) { - if (enabled) { - lockScreenEnabled.setEnabled(true); - Util.registerMediaButtonEventReceiver(getActivity(), false); - } else { - lockScreenEnabled.setEnabled(false); - Util.unregisterMediaButtonEventReceiver(getActivity(), false); - } + lockScreenEnabled.setEnabled(enabled); + Util.updateMediaButtonEventReceiver(); } private void setBluetoothPreferences(boolean enabled) { diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.java b/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.java index c86362a4..faa7f06d 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.java @@ -29,7 +29,7 @@ import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Util; /** - * Request media button focus when connected to Bluetooth A2DP. + * Resume or pause playback on Bluetooth A2DP connect/disconnect. * * @author Sindre Mehus */ @@ -63,7 +63,6 @@ public class BluetoothIntentReceiver extends BroadcastReceiver if (state == android.bluetooth.BluetoothA2dp.STATE_CONNECTED) actionA2dpConnected = true; else if (state == android.bluetooth.BluetoothA2dp.STATE_DISCONNECTED) actionA2dpDisconnected = true; - boolean connected = actionA2dpConnected || actionBluetoothDeviceConnected; boolean resume = false; boolean pause = false; @@ -83,12 +82,6 @@ public class BluetoothIntentReceiver extends BroadcastReceiver break; } - if (connected) - { - Timber.i("Connected to Bluetooth device %s address %s, requesting media button focus.", name, address); - Util.registerMediaButtonEventReceiver(context, false); - } - if (resume) { Timber.i("Connected to Bluetooth device %s address %s, resuming playback.", name, address); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/MediaButtonIntentReceiver.java b/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/MediaButtonIntentReceiver.java index 836eb5e7..9d201b52 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/MediaButtonIntentReceiver.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/MediaButtonIntentReceiver.java @@ -46,7 +46,7 @@ public class MediaButtonIntentReceiver extends BroadcastReceiver String intentAction = intent.getAction(); // If media button are turned off and we received a media button, exit - if (!Util.getMediaButtonsPreference(context) && + if (!Util.getMediaButtonsEnabled(context) && Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) return; // Only process media buttons and CMD_PROCESS_KEYCODE, which is received from the widgets diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerControllerImpl.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerControllerImpl.java index 662b0da1..ca300dc9 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerControllerImpl.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerControllerImpl.java @@ -85,12 +85,7 @@ public class MediaPlayerControllerImpl implements MediaPlayerController public void onCreate() { if (created) return; - this.externalStorageMonitor.onCreate(new Runnable() { - @Override - public void run() { - reset(); - } - }); + this.externalStorageMonitor.onCreate(this::reset); setJukeboxEnabled(activeServerProvider.getValue().getActiveServer().getJukeboxByDefault()); created = true; @@ -116,9 +111,8 @@ public class MediaPlayerControllerImpl implements MediaPlayerController if (currentPlayingIndex != -1) { - MediaPlayerService.executeOnStartedMediaPlayerService(context, new Consumer() { - @Override - public void accept(MediaPlayerService mediaPlayerService) { + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> + { mediaPlayerService.play(currentPlayingIndex, autoPlayStart); if (localMediaPlayer.currentPlaying != null) @@ -136,8 +130,9 @@ public class MediaPlayerControllerImpl implements MediaPlayerController } } autoPlayStart = false; - } - }); + return null; + } + ); } } @@ -149,55 +144,53 @@ public class MediaPlayerControllerImpl implements MediaPlayerController @Override public synchronized void play(final int index) { - MediaPlayerService.executeOnStartedMediaPlayerService(context,new Consumer() { - @Override - public void accept(MediaPlayerService mediaPlayerService) { + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { mediaPlayerService.play(index, true); - } - }); + return null; + } + ); } public synchronized void play() { - MediaPlayerService.executeOnStartedMediaPlayerService(context, new Consumer() { - @Override - public void accept(MediaPlayerService mediaPlayerService) { + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { + mediaPlayerService.play(); - } - }); + return null; + } + ); } public synchronized void resumeOrPlay() { - MediaPlayerService.executeOnStartedMediaPlayerService(context, new Consumer() { - @Override - public void accept(MediaPlayerService mediaPlayerService) { + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { mediaPlayerService.resumeOrPlay(); - } - }); + return null; + } + ); } + @Override public synchronized void togglePlayPause() { if (localMediaPlayer.playerState == PlayerState.IDLE) autoPlayStart = true; - MediaPlayerService.executeOnStartedMediaPlayerService(context,new Consumer() { - @Override - public void accept(MediaPlayerService mediaPlayerService) { + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { mediaPlayerService.togglePlayPause(); - } - }); + return null; + } + ); } + @Override public synchronized void start() { - MediaPlayerService.executeOnStartedMediaPlayerService(context, new Consumer() { - @Override - public void accept(MediaPlayerService mediaPlayerService) { + MediaPlayerService.executeOnStartedMediaPlayerService(context, (mediaPlayerService) -> { mediaPlayerService.start(); - } - }); + return null; + } + ); } @Override @@ -612,19 +605,14 @@ public class MediaPlayerControllerImpl implements MediaPlayerController final Entry song = localMediaPlayer.currentPlaying.getSong(); song.setUserRating(rating); - new Thread(new Runnable() - { - @Override - public void run() + new Thread(() -> { + try { - try - { - MusicServiceFactory.getMusicService(context).setRating(song.getId(), rating, context); - } - catch (Exception e) - { - Timber.e(e); - } + MusicServiceFactory.getMusicService(context).setRating(song.getId(), rating, context); + } + catch (Exception e) + { + Timber.e(e); } }).start(); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java index 2cf3d392..c4865d76 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java @@ -76,9 +76,6 @@ public class MediaPlayerLifecycleSupport registerHeadsetReceiver(); - // React to media buttons. - Util.registerMediaButtonEventReceiver(context, true); - mediaPlayerController.onCreate(); if (autoPlay) mediaPlayerController.preload(); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java index cf4a13f3..4884a6a6 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java @@ -31,7 +31,6 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.media.AudioManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiManager; @@ -53,8 +52,8 @@ import org.moire.ultrasonic.R; import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.domain.*; import org.moire.ultrasonic.domain.MusicDirectory.Entry; -import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver; import org.moire.ultrasonic.service.DownloadFile; +import org.moire.ultrasonic.service.MediaPlayerService; import java.io.*; import java.security.MessageDigest; @@ -678,31 +677,16 @@ public class Util return Bitmap.createScaledBitmap(bitmap, size, getScaledHeight(bitmap, size), true); } - public static void registerMediaButtonEventReceiver(Context context, boolean isService) + // Trigger an update on the MediaSession. Depending on the preference it will register + // or deregister the MediaButtonReceiver. + public static void updateMediaButtonEventReceiver() { - if (getMediaButtonsPreference(context)) - { - if (isService) mediaButtonsRegisteredForService = true; - else mediaButtonsRegisteredForUI = true; - - AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - audioManager.registerMediaButtonEventReceiver(new ComponentName(context.getPackageName(), MediaButtonIntentReceiver.class.getName())); + MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); + if (mediaPlayerService != null) { + mediaPlayerService.updateMediaButtonReceiver(); } } - public static void unregisterMediaButtonEventReceiver(Context context, boolean isService) - { - if (isService) mediaButtonsRegisteredForService = false; - else mediaButtonsRegisteredForUI = false; - - // Do not unregister while there is an active part of the app which needs the control - if (mediaButtonsRegisteredForService || mediaButtonsRegisteredForUI) return; - - AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - audioManager.unregisterMediaButtonEventReceiver(new ComponentName(context.getPackageName(), MediaButtonIntentReceiver.class.getName())); - Timber.i("MediaButtonEventReceiver unregistered."); - } - public static MusicDirectory getSongsFromSearchResult(SearchResult searchResult) { MusicDirectory musicDirectory = new MusicDirectory(); @@ -1055,7 +1039,7 @@ public class Util return Integer.parseInt(preferences.getString(Constants.PREFERENCES_KEY_INCREMENT_TIME, "5")); } - public static boolean getMediaButtonsPreference(Context context) + public static boolean getMediaButtonsEnabled(Context context) { SharedPreferences preferences = getPreferences(context); return preferences.getBoolean(Constants.PREFERENCES_KEY_MEDIA_BUTTONS, true); diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt index 97c274be..b113de4a 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt @@ -178,7 +178,7 @@ class NavigationActivity : AppCompatActivity() { super.onResume() setMenuForServerSetting() - Util.registerMediaButtonEventReceiver(this, false) + // Lifecycle support's constructor registers some event receivers so it should be created early lifecycleSupport.onCreate() @@ -188,7 +188,6 @@ class NavigationActivity : AppCompatActivity() { override fun onDestroy() { super.onDestroy() - Util.unregisterMediaButtonEventReceiver(this, false) nowPlayingEventDistributor.unsubscribe(nowPlayingEventListener) themeChangedEventDistributor.unsubscribe(themeChangedEventListener) imageLoaderProvider.clearImageLoader() @@ -310,7 +309,6 @@ class NavigationActivity : AppCompatActivity() { private fun exit() { lifecycleSupport.onDestroy() - Util.unregisterMediaButtonEventReceiver(this, false) finish() } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt index 612383a1..2332d54e 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt @@ -119,7 +119,6 @@ class LocalMediaPlayer( }.start() wakeLock.setReferenceCounted(false) - Util.registerMediaButtonEventReceiver(context, true) Timber.i("LocalMediaPlayer created") } @@ -146,7 +145,7 @@ class LocalMediaPlayer( if (nextPlayingTask != null) { nextPlayingTask!!.cancel() } - Util.unregisterMediaButtonEventReceiver(context, true) + wakeLock.release() } catch (exception: Throwable) { Timber.w(exception, "LocalMediaPlayer onDestroy exception: ") diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt index 9f8d8578..94f05c8b 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt @@ -12,6 +12,7 @@ import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.app.Service +import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.Build @@ -22,7 +23,6 @@ import android.support.v4.media.session.PlaybackStateCompat import android.view.KeyEvent import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import java.util.ArrayList import org.koin.android.ext.android.inject import org.moire.ultrasonic.R import org.moire.ultrasonic.activity.NavigationActivity @@ -33,6 +33,7 @@ import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X1 import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X2 import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X3 import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X4 +import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.FileUtil @@ -792,7 +793,8 @@ class MediaPlayerService : Service() { mediaSession = MediaSessionCompat(applicationContext, "UltrasonicService") mediaSessionToken = mediaSession!!.sessionToken - // mediaController = new MediaControllerCompat(getApplicationContext(), mediaSessionToken); + + updateMediaButtonReceiver() mediaSession!!.setCallback(object : MediaSessionCompat.Callback() { override fun onPlay() { @@ -838,10 +840,39 @@ class MediaPlayerService : Service() { ) } + fun updateMediaButtonReceiver() { + if (Util.getMediaButtonsEnabled(applicationContext)) { + registerMediaButtonEventReceiver() + } else { + unregisterMediaButtonEventReceiver() + } + } + + fun registerMediaButtonEventReceiver() { + val component = ComponentName(packageName, MediaButtonIntentReceiver::class.java.name) + val mediaButtonIntent = Intent(Intent.ACTION_MEDIA_BUTTON) + mediaButtonIntent.component = component + + val pendingIntent = PendingIntent.getBroadcast( + this, + INTENT_CODE_MEDIA_BUTTON, + mediaButtonIntent, + PendingIntent.FLAG_CANCEL_CURRENT + ) + + mediaSession?.setMediaButtonReceiver(pendingIntent) + } + + fun unregisterMediaButtonEventReceiver() { + mediaSession?.setMediaButtonReceiver(null) + } + companion object { private const val NOTIFICATION_CHANNEL_ID = "org.moire.ultrasonic" private const val NOTIFICATION_CHANNEL_NAME = "Ultrasonic background service" private const val NOTIFICATION_ID = 3033 + private const val INTENT_CODE_MEDIA_BUTTON = 161 + private var instance: MediaPlayerService? = null private val instanceLock = Any() @@ -872,7 +903,7 @@ class MediaPlayerService : Service() { @JvmStatic fun executeOnStartedMediaPlayerService( context: Context, - taskToExecute: Consumer + taskToExecute: (MediaPlayerService?) -> Unit ) { val t: Thread = object : Thread() { @@ -882,7 +913,7 @@ class MediaPlayerService : Service() { Timber.e("ExecuteOnStarted.. failed to get a MediaPlayerService instance!") return } - taskToExecute.accept(instance) + taskToExecute(instance) } } t.start()