diff --git a/ultrasonic/src/main/AndroidManifest.xml b/ultrasonic/src/main/AndroidManifest.xml index 4957fe6d..98132a26 100644 --- a/ultrasonic/src/main/AndroidManifest.xml +++ b/ultrasonic/src/main/AndroidManifest.xml @@ -126,14 +126,6 @@ android:name=".service.MediaPlayerService" android:label="Ultrasonic Download Service" android:exported="false"> - - - - - - - - @@ -141,6 +133,17 @@ + + + + + + + + + + + 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 5c70c13c..a9791d67 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/BluetoothIntentReceiver.java @@ -94,13 +94,13 @@ public class BluetoothIntentReceiver extends BroadcastReceiver if (resume) { Log.i(TAG, String.format("Connected to Bluetooth device %s address %s, resuming playback.", name, address)); - context.sendBroadcast(new Intent(Constants.CMD_PLAY)); + context.sendBroadcast(new Intent(Constants.CMD_RESUME_OR_PLAY).setPackage(context.getPackageName())); } if (pause) { Log.i(TAG, String.format("Disconnected from Bluetooth device %s address %s, requesting pause.", name, address)); - context.sendBroadcast(new Intent(Constants.CMD_PAUSE)); + context.sendBroadcast(new Intent(Constants.CMD_PAUSE).setPackage(context.getPackageName())); } } } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/UltrasonicIntentReceiver.java b/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/UltrasonicIntentReceiver.java new file mode 100644 index 00000000..7b8ba2ec --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/receiver/UltrasonicIntentReceiver.java @@ -0,0 +1,39 @@ +package org.moire.ultrasonic.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport; + +import kotlin.Lazy; + +import static org.koin.java.KoinJavaComponent.inject; + +public class UltrasonicIntentReceiver extends BroadcastReceiver +{ + private static final String TAG = UltrasonicIntentReceiver.class.getSimpleName(); + private Lazy lifecycleSupport = inject(MediaPlayerLifecycleSupport.class); + + @Override + public void onReceive(Context context, Intent intent) + { + String intentAction = intent.getAction(); + Log.i(TAG, String.format("Received Ultrasonic Intent: %s", intentAction)); + + try + { + lifecycleSupport.getValue().receiveIntent(intent); + + if (isOrderedBroadcast()) + { + abortBroadcast(); + } + } + catch (Exception x) + { + // Ignored. + } + } +} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java index ce016bd1..64823cfd 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Downloader.java @@ -89,7 +89,7 @@ public class Downloader public void stop() { - executorService.shutdown(); + if (executorService != null) executorService.shutdown(); Log.i(TAG, "Downloader stopped"); } 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 bbe29d7e..26c95f45 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerControllerImpl.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerControllerImpl.java @@ -170,6 +170,16 @@ public class MediaPlayerControllerImpl implements MediaPlayerController }); } + public synchronized void resumeOrPlay() + { + MediaPlayerService.executeOnStartedMediaPlayerService(context, new Consumer() { + @Override + public void accept(MediaPlayerService mediaPlayerService) { + mediaPlayerService.resumeOrPlay(); + } + }); + } + @Override public synchronized void togglePlayPause() { 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 7a53cbe0..ef7c18d8 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerLifecycleSupport.java @@ -81,17 +81,6 @@ public class MediaPlayerLifecycleSupport // React to media buttons. Util.registerMediaButtonEventReceiver(context, true); - // Register the handler for outside intents. - IntentFilter commandFilter = new IntentFilter(); - commandFilter.addAction(Constants.CMD_PLAY); - commandFilter.addAction(Constants.CMD_TOGGLEPAUSE); - commandFilter.addAction(Constants.CMD_PAUSE); - commandFilter.addAction(Constants.CMD_STOP); - commandFilter.addAction(Constants.CMD_PREVIOUS); - commandFilter.addAction(Constants.CMD_NEXT); - commandFilter.addAction(Constants.CMD_PROCESS_KEYCODE); - context.registerReceiver(intentReceiver, commandFilter); - mediaPlayerController.onCreate(); if (autoPlay) mediaPlayerController.preload(); @@ -120,7 +109,6 @@ public class MediaPlayerLifecycleSupport downloader.getCurrentPlayingIndex(), mediaPlayerController.getPlayerPosition()); mediaPlayerController.clear(false); context.unregisterReceiver(headsetEventReceiver); - context.unregisterReceiver(intentReceiver); mediaPlayerController.onDestroy(); created = false; Log.i(TAG, "LifecycleSupport destroyed"); @@ -128,19 +116,33 @@ public class MediaPlayerLifecycleSupport public void receiveIntent(Intent intent) { - Log.i(TAG, "Received intent"); - if (intent != null && intent.getExtras() != null) - { - KeyEvent event = (KeyEvent) intent.getExtras().get(Intent.EXTRA_KEY_EVENT); - if (event != null) - { - handleKeyEvent(event); + if (intent == null) return; + String intentAction = intent.getAction(); + if (intentAction == null || intentAction.isEmpty()) return; + + Log.i(TAG, String.format("Received intent: %s", intentAction)); + + if (intentAction.equals(Constants.CMD_PROCESS_KEYCODE)) { + if (intent.getExtras() != null) { + KeyEvent event = (KeyEvent) intent.getExtras().get(Intent.EXTRA_KEY_EVENT); + if (event != null) { + handleKeyEvent(event); + } } } + else + { + handleUltrasonicIntent(intentAction); + } } + /** + * The Headset Intent Receiver is responsible for resuming playback when a headset is inserted + * and pausing it when it is removed. + * Unfortunately this Intent can't be registered in the AndroidManifest, so it works only + * while Ultrasonic is running. + */ private void registerHeadsetReceiver() { - // Pause when headset is unplugged. final SharedPreferences sp = Util.getPreferences(context); final String spKey = context .getString(R.string.settings_playback_resume_play_on_headphones_plug); @@ -255,43 +257,53 @@ public class MediaPlayerLifecycleSupport } /** - * This receiver manages the intent that could come from other applications. + * This function processes the intent that could come from other applications. */ - private BroadcastReceiver intentReceiver = new BroadcastReceiver() + private void handleUltrasonicIntent(final String intentAction) { - @Override - public void onReceive(Context context, Intent intent) - { - String action = intent.getAction(); - if (action == null) return; - Log.i(TAG, "intentReceiver.onReceive: " + action); + final boolean isRunning = created; + // If Ultrasonic is not running, do nothing to stop or pause + if (!isRunning && (intentAction.equals(Constants.CMD_PAUSE) || + intentAction.equals(Constants.CMD_STOP))) return; - switch(action) - { - case Constants.CMD_PLAY: - mediaPlayerController.play(); - break; - case Constants.CMD_NEXT: - mediaPlayerController.next(); - break; - case Constants.CMD_PREVIOUS: - mediaPlayerController.previous(); - break; - case Constants.CMD_TOGGLEPAUSE: - mediaPlayerController.togglePlayPause(); - break; - case Constants.CMD_STOP: - // TODO: There is a stop() function, shouldn't we use that? - mediaPlayerController.pause(); - mediaPlayerController.seekTo(0); - break; - case Constants.CMD_PAUSE: - mediaPlayerController.pause(); - break; - case Constants.CMD_PROCESS_KEYCODE: - receiveIntent(intent); - break; + boolean autoStart = (intentAction.equals(Constants.CMD_PLAY) || + intentAction.equals(Constants.CMD_RESUME_OR_PLAY) || + intentAction.equals(Constants.CMD_TOGGLEPAUSE) || + intentAction.equals(Constants.CMD_PREVIOUS) || + intentAction.equals(Constants.CMD_NEXT)); + + // We can receive intents when everything is stopped, so we need to start + onCreate(autoStart, new Runnable() { + @Override + public void run() { + switch(intentAction) + { + case Constants.CMD_PLAY: + mediaPlayerController.play(); + break; + case Constants.CMD_RESUME_OR_PLAY: + // If Ultrasonic wasn't running, the autoStart is enough to resume, no need to call anything + if (isRunning) mediaPlayerController.resumeOrPlay(); + break; + case Constants.CMD_NEXT: + mediaPlayerController.next(); + break; + case Constants.CMD_PREVIOUS: + mediaPlayerController.previous(); + break; + case Constants.CMD_TOGGLEPAUSE: + mediaPlayerController.togglePlayPause(); + break; + case Constants.CMD_STOP: + // TODO: There is a stop() function, shouldn't we use that? + mediaPlayerController.pause(); + mediaPlayerController.seekTo(0); + break; + case Constants.CMD_PAUSE: + mediaPlayerController.pause(); + break; + } } - } - }; + }); + } } \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerService.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerService.java index 3fc6de35..5b64ddc6 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MediaPlayerService.java @@ -361,6 +361,18 @@ public class MediaPlayerService extends Service } } + public synchronized void resumeOrPlay() + { + if (localMediaPlayer.playerState == PAUSED || localMediaPlayer.playerState == COMPLETED || localMediaPlayer.playerState == STOPPED) + { + start(); + } + else if (localMediaPlayer.playerState == IDLE) + { + play(); + } + } + /** * Plays either the current song (resume) or the first/next one in queue. */ diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java index 3b330ec3..07355b94 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java @@ -63,6 +63,7 @@ public final class Constants // Names for Intent Actions public static final String CMD_PROCESS_KEYCODE = "org.moire.ultrasonic.CMD_PROCESS_KEYCODE"; public static final String CMD_PLAY = "org.moire.ultrasonic.CMD_PLAY"; + public static final String CMD_RESUME_OR_PLAY = "org.moire.ultrasonic.CMD_RESUME_OR_PLAY"; public static final String CMD_TOGGLEPAUSE = "org.moire.ultrasonic.CMD_TOGGLEPAUSE"; public static final String CMD_PAUSE = "org.moire.ultrasonic.CMD_PAUSE"; public static final String CMD_STOP = "org.moire.ultrasonic.CMD_STOP";