diff --git a/res/values/strings.xml b/res/values/strings.xml
index dd581970..23d4e2df 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -569,4 +569,11 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
The notifications permission is needed so the phone can ring when the app is in the background
Notifications are disabled, you won\'t receive incoming pair notifications.
+
+ Continue playing
+ mpris_keepwatching_enabled
+ Continue playing
+ Show a silent notification to continue playing on this device after closing media
+ Continue playing
+
diff --git a/res/xml/mprisplugin_preferences.xml b/res/xml/mprisplugin_preferences.xml
index e29d7a06..2b522f49 100644
--- a/res/xml/mprisplugin_preferences.xml
+++ b/res/xml/mprisplugin_preferences.xml
@@ -28,4 +28,11 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
android:summary="@string/mpris_notification_settings_summary"
android:title="@string/mpris_notification_settings_title" />
+
+
diff --git a/src/org/kde/kdeconnect/Helpers/NotificationHelper.java b/src/org/kde/kdeconnect/Helpers/NotificationHelper.java
index 5ef6512d..aff10eb6 100644
--- a/src/org/kde/kdeconnect/Helpers/NotificationHelper.java
+++ b/src/org/kde/kdeconnect/Helpers/NotificationHelper.java
@@ -35,6 +35,8 @@ public class NotificationHelper {
public final static String RECEIVENOTIFICATION = "receive";
public final static String HIGHPRIORITY = "highpriority";
+ public final static String CONTINUEWATCHING = "continuewatching";
+
}
public static void notifyCompat(NotificationManager notificationManager, int notificationId, Notification notification) {
@@ -87,14 +89,23 @@ public class NotificationHelper {
.Builder(Channels.RECEIVENOTIFICATION, NotificationManagerCompat.IMPORTANCE_DEFAULT)
.setName(context.getString(R.string.notification_channel_receivenotification))
.build();
- final NotificationChannelCompat highPriorityChannel = new NotificationChannelCompat
+ final NotificationChannelCompat continueWatchingChannel = new NotificationChannelCompat
.Builder(Channels.HIGHPRIORITY, NotificationManagerCompat.IMPORTANCE_HIGH)
.setName(context.getString(R.string.notification_channel_high_priority))
.build();
-
+ /* This notification should be highly visible *only* if the user looks at their phone */
+ /* It should not be a distraction. It should be a convenient button to press */
+ final NotificationChannelCompat highPriorityChannel = new NotificationChannelCompat
+ .Builder(Channels.CONTINUEWATCHING, NotificationManagerCompat.IMPORTANCE_HIGH)
+ .setName(context.getString(R.string.notification_channel_keepwatching))
+ .setVibrationEnabled(false)
+ .setLightsEnabled(false)
+ .setSound(null, null)
+ .build();
final List channels = Arrays.asList(persistentChannel,
defaultChannel, mediaChannel, fileTransferDownloadChannel, fileTransferUploadChannel,
- fileTransferErrorChannel, receiveNotificationChannel, highPriorityChannel);
+ fileTransferErrorChannel, receiveNotificationChannel, highPriorityChannel,
+ continueWatchingChannel);
NotificationManagerCompat.from(context).createNotificationChannelsCompat(channels);
@@ -102,7 +113,7 @@ public class NotificationHelper {
// Use this to deprecate old channels.
NotificationManagerCompat.from(context).deleteUnlistedNotificationChannels(
channels.stream()
- .map(notificationChannelCompat -> notificationChannelCompat.getId())
+ .map(NotificationChannelCompat::getId)
.collect(Collectors.toList()));
}
diff --git a/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisPlugin.kt b/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisPlugin.kt
index e6ef070b..ca485bd6 100644
--- a/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisPlugin.kt
+++ b/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisPlugin.kt
@@ -7,12 +7,20 @@ package org.kde.kdeconnect.Plugins.MprisPlugin
import android.Manifest
import android.app.Activity
+import android.app.NotificationManager
+import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
+import android.net.Uri
import android.os.Build
import android.util.Log
import androidx.annotation.DrawableRes
+import androidx.core.app.NotificationCompat
+import androidx.core.content.ContextCompat
+import androidx.preference.PreferenceManager
+import org.kde.kdeconnect.Helpers.NotificationHelper
+import org.kde.kdeconnect.Helpers.VideoUrlsHelper
import org.kde.kdeconnect.NetworkPacket
import org.kde.kdeconnect.Plugins.MprisPlugin.AlbumArtCache.deregisterPlugin
import org.kde.kdeconnect.Plugins.MprisPlugin.AlbumArtCache.getAlbumArt
@@ -243,6 +251,7 @@ class MprisPlugin : Plugin() {
if (np.has("player")) {
val playerStatus = players[np.getString("player")]
if (playerStatus != null) {
+ val wasPlaying = playerStatus.isPlaying
//Note: title, artist and album will not be available for all desktop clients
playerStatus.title = np.getString("title", playerStatus.title)
playerStatus.artist = np.getString("artist", playerStatus.artist)
@@ -286,6 +295,11 @@ class MprisPlugin : Plugin() {
playerStatusUpdated.remove(key)
}
}
+
+ // Check to see if a stream has stopped playing and we should deliver a notification
+ if (np.has("isPlaying") && !playerStatus.isPlaying && wasPlaying) {
+ showContinueWatchingNotification(playerStatus)
+ }
}
}
@@ -309,8 +323,13 @@ class MprisPlugin : Plugin() {
val oldPlayer = it.key
val found = newPlayerList.stream().anyMatch { newPlayer -> newPlayer == oldPlayer }
if (!found) {
- iter.remove()
+ // Player got removed
equals = false
+ iter.remove()
+ val playerStatus = it.value
+ if (playerStatus.isPlaying) {
+ showContinueWatchingNotification(playerStatus)
+ }
}
}
if (!equals) {
@@ -328,6 +347,34 @@ class MprisPlugin : Plugin() {
return true
}
+ private fun showContinueWatchingNotification(playerStatus: MprisPlayer) {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ if (prefs.getBoolean(context.getString(R.string.mpris_keepwatching_key), true) &&
+ (playerStatus.url.startsWith("http://") || playerStatus.url.startsWith("https://"))
+ ) {
+ try {
+ val url = VideoUrlsHelper.formatUriWithSeek(playerStatus.url, playerStatus.position).toString()
+ val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
+ val pendingIntent = PendingIntent.getActivity(device.context, 0, browserIntent, PendingIntent.FLAG_IMMUTABLE)
+
+ val notificationManager = ContextCompat.getSystemService(device.context, NotificationManager::class.java)
+ val builder = NotificationCompat.Builder(device.context, NotificationHelper.Channels.CONTINUEWATCHING)
+ .setContentTitle(context.resources.getString(R.string.kde_connect))
+ .setSmallIcon(R.drawable.ic_play_white)
+ .setTimeoutAfter(3000)
+ .setContentIntent(pendingIntent)
+ .setContentText(context.resources.getString(R.string.mpris_keepwatching) + " " + playerStatus.title)
+ NotificationHelper.notifyCompat(
+ notificationManager,
+ System.currentTimeMillis().toInt(),
+ builder.build()
+ )
+ } catch (e: MalformedURLException) {
+ e.printStackTrace();
+ }
+ }
+ }
+
override val supportedPacketTypes: Array = arrayOf(PACKET_TYPE_MPRIS)
override val outgoingPacketTypes: Array = arrayOf(PACKET_TYPE_MPRIS_REQUEST)