2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-22 09:58:08 +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="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>

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: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>

View File

@ -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<NotificationChannelCompat> 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()));
}

View File

@ -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<String> = arrayOf(PACKET_TYPE_MPRIS)
override val outgoingPacketTypes: Array<String> = arrayOf(PACKET_TYPE_MPRIS_REQUEST)