diff --git a/audio_service/android/src/main/java/com/ryanheise/audioservice/AudioService.java b/audio_service/android/src/main/java/com/ryanheise/audioservice/AudioService.java index a7765049..c794b7ce 100644 --- a/audio_service/android/src/main/java/com/ryanheise/audioservice/AudioService.java +++ b/audio_service/android/src/main/java/com/ryanheise/audioservice/AudioService.java @@ -221,6 +221,7 @@ private static int calculateInSampleSize(BitmapFactory.Options options, int reqW private boolean notificationCreated; private final Handler handler = new Handler(Looper.getMainLooper()); private VolumeProviderCompat volumeProvider; + private boolean isInForeground; public AudioProcessingState getProcessingState() { return processingState; @@ -404,9 +405,9 @@ else if (errorMessage != null) mediaSession.setShuffleMode(shuffleMode); mediaSession.setCaptioningEnabled(captioningEnabled); - if (!wasPlaying && playing) { + if (!isInForeground && isActuallyPlaying()) { enterPlayingState(); - } else if (wasPlaying && !playing) { + } else if (isInForeground && !playing) { exitPlayingState(); } @@ -489,11 +490,12 @@ private Notification buildNotification() { final MediaStyle style = new MediaStyle() .setMediaSession(mediaSession.getSessionToken()) .setShowActionsInCompactView(compactActionIndices); - if (config.androidNotificationOngoing) { - style.setShowCancelButton(true); - style.setCancelButtonIntent(buildMediaButtonPendingIntent(PlaybackStateCompat.ACTION_STOP)); - builder.setOngoing(true); - } + boolean ongoing = (config.androidNotificationOngoing != null) + ? config.androidNotificationOngoing + : isActuallyPlaying(); + style.setShowCancelButton(!ongoing); + style.setCancelButtonIntent(buildMediaButtonPendingIntent(PlaybackStateCompat.ACTION_STOP)); + builder.setOngoing(ongoing); builder.setStyle(style); return builder.build(); } @@ -548,6 +550,12 @@ private void updateNotification() { } } + private boolean isActuallyPlaying() { + return playing && (processingState == AudioProcessingState.loading + || processingState == AudioProcessingState.buffering + || processingState == AudioProcessingState.ready); + } + private void enterPlayingState() { ContextCompat.startForegroundService(this, new Intent(AudioService.this, AudioService.class)); if (!mediaSession.isActive()) @@ -555,7 +563,9 @@ private void enterPlayingState() { acquireWakeLock(); mediaSession.setSessionActivity(contentIntent); - internalStartForeground(); + startForeground(NOTIFICATION_ID, buildNotification()); + notificationCreated = true; + isInForeground = true; } private void exitPlayingState() { @@ -567,11 +577,7 @@ private void exitPlayingState() { private void exitForegroundState() { stopForeground(false); releaseWakeLock(); - } - - private void internalStartForeground() { - startForeground(NOTIFICATION_ID, buildNotification()); - notificationCreated = true; + isInForeground = false; } private void acquireWakeLock() { @@ -595,6 +601,7 @@ private void deactivateMediaSession() { } // Force cancellation of the notification getNotificationManager().cancel(NOTIFICATION_ID); + notificationCreated = false; } private void releaseMediaSession() { diff --git a/audio_service/android/src/main/java/com/ryanheise/audioservice/AudioServiceConfig.java b/audio_service/android/src/main/java/com/ryanheise/audioservice/AudioServiceConfig.java index 1917989d..ff64252b 100644 --- a/audio_service/android/src/main/java/com/ryanheise/audioservice/AudioServiceConfig.java +++ b/audio_service/android/src/main/java/com/ryanheise/audioservice/AudioServiceConfig.java @@ -33,7 +33,7 @@ public class AudioServiceConfig { public String androidNotificationIcon; public boolean androidShowNotificationBadge; public boolean androidNotificationClickStartsActivity; - public boolean androidNotificationOngoing; + public Boolean androidNotificationOngoing; public boolean androidStopForegroundOnPause; public int artDownscaleWidth; public int artDownscaleHeight; @@ -50,7 +50,7 @@ public AudioServiceConfig(Context context) { androidNotificationIcon = preferences.getString(KEY_ANDROID_NOTIFICATION_ICON, "mipmap/ic_launcher"); androidShowNotificationBadge = preferences.getBoolean(KEY_ANDROID_SHOW_NOTIFICATION_BADGE, false); androidNotificationClickStartsActivity = preferences.getBoolean(KEY_ANDROID_NOTIFICATION_CLICK_STARTS_ACTIVITY, true); - androidNotificationOngoing = preferences.getBoolean(KEY_ANDROID_NOTIFICATION_ONGOING, false); + androidNotificationOngoing = !preferences.contains(KEY_ANDROID_NOTIFICATION_ONGOING) ? null : new Boolean(preferences.getBoolean(KEY_ANDROID_NOTIFICATION_ONGOING, false)); androidStopForegroundOnPause = preferences.getBoolean(KEY_ANDROID_STOP_FOREGROUND_ON_PAUSE, true); artDownscaleWidth = preferences.getInt(KEY_ART_DOWNSCALE_WIDTH, -1); artDownscaleHeight = preferences.getInt(KEY_ART_DOWNSCALE_HEIGHT, -1); @@ -100,7 +100,7 @@ public Bundle getBrowsableRootExtras() { } public void save() { - preferences.edit() + final SharedPreferences.Editor editor = preferences.edit() .putBoolean(KEY_ANDROID_RESUME_ON_CLICK, androidResumeOnClick) .putString(KEY_ANDROID_NOTIFICATION_CHANNEL_ID, androidNotificationChannelId) .putString(KEY_ANDROID_NOTIFICATION_CHANNEL_NAME, androidNotificationChannelName) @@ -114,7 +114,11 @@ public void save() { .putInt(KEY_ART_DOWNSCALE_WIDTH, artDownscaleWidth) .putInt(KEY_ART_DOWNSCALE_HEIGHT, artDownscaleHeight) .putString(KEY_ACTIVITY_CLASS_NAME, activityClassName) - .putString(KEY_BROWSABLE_ROOT_EXTRAS, browsableRootExtras) - .apply(); + .putString(KEY_BROWSABLE_ROOT_EXTRAS, browsableRootExtras); + if (androidNotificationOngoing != null) + editor.putBoolean(KEY_ANDROID_NOTIFICATION_ONGOING, androidNotificationOngoing); + else + editor.remove(KEY_ANDROID_NOTIFICATION_ONGOING); + editor.apply(); } } diff --git a/audio_service/example/lib/example_multiple_handlers.dart b/audio_service/example/lib/example_multiple_handlers.dart index 45eb3c3f..4d8acf90 100644 --- a/audio_service/example/lib/example_multiple_handlers.dart +++ b/audio_service/example/lib/example_multiple_handlers.dart @@ -47,7 +47,6 @@ Future main() async { config: const AudioServiceConfig( androidNotificationChannelId: 'com.ryanheise.myapp.channel.audio', androidNotificationChannelName: 'Audio playback', - androidNotificationOngoing: true, ), ); runApp(MyApp()); diff --git a/audio_service/example/lib/example_playlist.dart b/audio_service/example/lib/example_playlist.dart index 09d0b4a3..42947cfe 100644 --- a/audio_service/example/lib/example_playlist.dart +++ b/audio_service/example/lib/example_playlist.dart @@ -26,7 +26,6 @@ Future main() async { config: const AudioServiceConfig( androidNotificationChannelId: 'com.ryanheise.myapp.channel.audio', androidNotificationChannelName: 'Audio playback', - androidNotificationOngoing: true, ), ); runApp(MyApp()); diff --git a/audio_service/example/lib/main.dart b/audio_service/example/lib/main.dart index 69583141..1fb19f59 100644 --- a/audio_service/example/lib/main.dart +++ b/audio_service/example/lib/main.dart @@ -36,7 +36,6 @@ Future main() async { config: const AudioServiceConfig( androidNotificationChannelId: 'com.ryanheise.myapp.channel.audio', androidNotificationChannelName: 'Audio playback', - androidNotificationOngoing: true, ), ); runApp(MyApp()); diff --git a/audio_service/lib/audio_service.dart b/audio_service/lib/audio_service.dart index 1e812c3c..49c5b51b 100644 --- a/audio_service/lib/audio_service.dart +++ b/audio_service/lib/audio_service.dart @@ -3305,7 +3305,11 @@ class AudioServiceConfig { /// If you set this to true, [androidStopForegroundOnPause] must be true as well, /// otherwise this will not do anything, because when foreground service is active, /// it forces notification to be ongoing. - final bool androidNotificationOngoing; + /// + /// Leave this unset if you would like the plugin to automatically set the + /// ongoing status whenever your handler is playing audio (i.e. + /// [PlaybackState.playing] and [AudioProcessingState.ready] are true.) + final bool? androidNotificationOngoing; /// Whether the Android service should switch to a lower priority state when /// playback is paused allowing the user to swipe away the notification. Note @@ -3354,7 +3358,7 @@ class AudioServiceConfig { this.androidNotificationIcon = 'mipmap/ic_launcher', this.androidShowNotificationBadge = false, this.androidNotificationClickStartsActivity = true, - this.androidNotificationOngoing = false, + this.androidNotificationOngoing, this.androidStopForegroundOnPause = true, this.artDownscaleWidth, this.artDownscaleHeight, @@ -3364,7 +3368,9 @@ class AudioServiceConfig { this.androidBrowsableRootExtras, }) : assert((artDownscaleWidth != null) == (artDownscaleHeight != null)), assert( - !androidNotificationOngoing || androidStopForegroundOnPause, + androidNotificationOngoing == null || + !androidNotificationOngoing || + androidStopForegroundOnPause, 'The androidNotificationOngoing will make no effect with androidStopForegroundOnPause set to false', ); @@ -3379,7 +3385,9 @@ class AudioServiceConfig { androidShowNotificationBadge: androidShowNotificationBadge, androidNotificationClickStartsActivity: androidNotificationClickStartsActivity, - androidNotificationOngoing: androidNotificationOngoing, + // TODO: update AudioServiceConfigMessage.androidNotificationOngoing + // to be nullable. + androidNotificationOngoing: androidNotificationOngoing ?? false, androidStopForegroundOnPause: androidStopForegroundOnPause, artDownscaleWidth: artDownscaleWidth, artDownscaleHeight: artDownscaleHeight,