2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-22 18:07:55 +00:00

Offer to "continue playing" media on this device after pausing

Based on the MR !249

Co-authored-by: Alex Gravenor <blazingkin@gmail.com>
This commit is contained in:
Albert Vaca Cintora 2024-05-02 16:14:45 +02:00
parent 411bcc3960
commit 3d166e6d4b
No known key found for this signature in database
4 changed files with 77 additions and 5 deletions

View File

@ -569,4 +569,11 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
<string name="findmyphone_notifications_explanation">The notifications permission is needed so the phone can ring when the app is in the background</string> <string name="findmyphone_notifications_explanation">The notifications permission is needed so the phone can ring when the app is in the background</string>
<string name="no_notifications">Notifications are disabled, you won\'t receive incoming pair notifications.</string> <string name="no_notifications">Notifications are disabled, you won\'t receive incoming pair notifications.</string>
<string name="mpris_keepwatching">Continue playing</string>
<string name="mpris_keepwatching_key" translatable="false">mpris_keepwatching_enabled</string>
<string name="mpris_keepwatching_settings_title">Continue playing</string>
<string name="mpris_keepwatching_settings_summary">Show a silent notification to continue playing on this device after closing media</string>
<string name="notification_channel_keepwatching">Continue playing</string>
</resources> </resources>

View File

@ -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:summary="@string/mpris_notification_settings_summary"
android:title="@string/mpris_notification_settings_title" /> android:title="@string/mpris_notification_settings_title" />
<SwitchPreference
android:id="@+id/mpris_keepwatching_preference"
android:defaultValue="true"
android:key="@string/mpris_keepwatching_key"
android:summary="@string/mpris_keepwatching_settings_summary"
android:title="@string/mpris_keepwatching_settings_title" />
</PreferenceScreen> </PreferenceScreen>

View File

@ -35,6 +35,8 @@ public class NotificationHelper {
public final static String RECEIVENOTIFICATION = "receive"; public final static String RECEIVENOTIFICATION = "receive";
public final static String HIGHPRIORITY = "highpriority"; public final static String HIGHPRIORITY = "highpriority";
public final static String CONTINUEWATCHING = "continuewatching";
} }
public static void notifyCompat(NotificationManager notificationManager, int notificationId, Notification notification) { public static void notifyCompat(NotificationManager notificationManager, int notificationId, Notification notification) {
@ -87,14 +89,23 @@ public class NotificationHelper {
.Builder(Channels.RECEIVENOTIFICATION, NotificationManagerCompat.IMPORTANCE_DEFAULT) .Builder(Channels.RECEIVENOTIFICATION, NotificationManagerCompat.IMPORTANCE_DEFAULT)
.setName(context.getString(R.string.notification_channel_receivenotification)) .setName(context.getString(R.string.notification_channel_receivenotification))
.build(); .build();
final NotificationChannelCompat highPriorityChannel = new NotificationChannelCompat final NotificationChannelCompat continueWatchingChannel = new NotificationChannelCompat
.Builder(Channels.HIGHPRIORITY, NotificationManagerCompat.IMPORTANCE_HIGH) .Builder(Channels.HIGHPRIORITY, NotificationManagerCompat.IMPORTANCE_HIGH)
.setName(context.getString(R.string.notification_channel_high_priority)) .setName(context.getString(R.string.notification_channel_high_priority))
.build(); .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<NotificationChannelCompat> channels = Arrays.asList(persistentChannel, final List<NotificationChannelCompat> channels = Arrays.asList(persistentChannel,
defaultChannel, mediaChannel, fileTransferDownloadChannel, fileTransferUploadChannel, defaultChannel, mediaChannel, fileTransferDownloadChannel, fileTransferUploadChannel,
fileTransferErrorChannel, receiveNotificationChannel, highPriorityChannel); fileTransferErrorChannel, receiveNotificationChannel, highPriorityChannel,
continueWatchingChannel);
NotificationManagerCompat.from(context).createNotificationChannelsCompat(channels); NotificationManagerCompat.from(context).createNotificationChannelsCompat(channels);
@ -102,7 +113,7 @@ public class NotificationHelper {
// Use this to deprecate old channels. // Use this to deprecate old channels.
NotificationManagerCompat.from(context).deleteUnlistedNotificationChannels( NotificationManagerCompat.from(context).deleteUnlistedNotificationChannels(
channels.stream() channels.stream()
.map(notificationChannelCompat -> notificationChannelCompat.getId()) .map(NotificationChannelCompat::getId)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }

View File

@ -7,12 +7,20 @@ package org.kde.kdeconnect.Plugins.MprisPlugin
import android.Manifest import android.Manifest
import android.app.Activity import android.app.Activity
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import androidx.annotation.DrawableRes 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.NetworkPacket
import org.kde.kdeconnect.Plugins.MprisPlugin.AlbumArtCache.deregisterPlugin import org.kde.kdeconnect.Plugins.MprisPlugin.AlbumArtCache.deregisterPlugin
import org.kde.kdeconnect.Plugins.MprisPlugin.AlbumArtCache.getAlbumArt import org.kde.kdeconnect.Plugins.MprisPlugin.AlbumArtCache.getAlbumArt
@ -243,6 +251,7 @@ class MprisPlugin : Plugin() {
if (np.has("player")) { if (np.has("player")) {
val playerStatus = players[np.getString("player")] val playerStatus = players[np.getString("player")]
if (playerStatus != null) { if (playerStatus != null) {
val wasPlaying = playerStatus.isPlaying
//Note: title, artist and album will not be available for all desktop clients //Note: title, artist and album will not be available for all desktop clients
playerStatus.title = np.getString("title", playerStatus.title) playerStatus.title = np.getString("title", playerStatus.title)
playerStatus.artist = np.getString("artist", playerStatus.artist) playerStatus.artist = np.getString("artist", playerStatus.artist)
@ -286,6 +295,11 @@ class MprisPlugin : Plugin() {
playerStatusUpdated.remove(key) 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 oldPlayer = it.key
val found = newPlayerList.stream().anyMatch { newPlayer -> newPlayer == oldPlayer } val found = newPlayerList.stream().anyMatch { newPlayer -> newPlayer == oldPlayer }
if (!found) { if (!found) {
iter.remove() // Player got removed
equals = false equals = false
iter.remove()
val playerStatus = it.value
if (playerStatus.isPlaying) {
showContinueWatchingNotification(playerStatus)
}
} }
} }
if (!equals) { if (!equals) {
@ -328,6 +347,34 @@ class MprisPlugin : Plugin() {
return true 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<String> = arrayOf(PACKET_TYPE_MPRIS) override val supportedPacketTypes: Array<String> = arrayOf(PACKET_TYPE_MPRIS)
override val outgoingPacketTypes: Array<String> = arrayOf(PACKET_TYPE_MPRIS_REQUEST) override val outgoingPacketTypes: Array<String> = arrayOf(PACKET_TYPE_MPRIS_REQUEST)