diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 02e1e178..2aa3db15 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -120,6 +120,9 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_device_laptop_shortcut.xml b/res/drawable/ic_device_laptop_shortcut.xml
new file mode 100644
index 00000000..b1569ee5
--- /dev/null
+++ b/res/drawable/ic_device_laptop_shortcut.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/res/drawable/ic_device_phone_shortcut.xml b/res/drawable/ic_device_phone_shortcut.xml
new file mode 100644
index 00000000..f2b83b61
--- /dev/null
+++ b/res/drawable/ic_device_phone_shortcut.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_device_tablet_shortcut.xml b/res/drawable/ic_device_tablet_shortcut.xml
new file mode 100644
index 00000000..fb214133
--- /dev/null
+++ b/res/drawable/ic_device_tablet_shortcut.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_device_tv_shortcut.xml b/res/drawable/ic_device_tv_shortcut.xml
new file mode 100644
index 00000000..d2006224
--- /dev/null
+++ b/res/drawable/ic_device_tv_shortcut.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/res/xml/shortcuts.xml b/res/xml/shortcuts.xml
new file mode 100644
index 00000000..d8cca870
--- /dev/null
+++ b/res/xml/shortcuts.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/org/kde/kdeconnect/DeviceInfo.kt b/src/org/kde/kdeconnect/DeviceInfo.kt
index e02c243e..32fcb1f1 100644
--- a/src/org/kde/kdeconnect/DeviceInfo.kt
+++ b/src/org/kde/kdeconnect/DeviceInfo.kt
@@ -22,13 +22,13 @@ import java.security.cert.CertificateException
* DeviceInfo contains all the properties needed to instantiate a Device.
*/
class DeviceInfo(
- @JvmField val id : String,
- @JvmField val certificate : Certificate,
- @JvmField var name : String,
- @JvmField var type : DeviceType,
- @JvmField var protocolVersion : Int = 0,
- @JvmField var incomingCapabilities : Set? = null,
- @JvmField var outgoingCapabilities : Set? = null,
+ @JvmField val id: String,
+ @JvmField val certificate: Certificate,
+ @JvmField var name: String,
+ @JvmField var type: DeviceType,
+ @JvmField var protocolVersion: Int = 0,
+ @JvmField var incomingCapabilities: Set? = null,
+ @JvmField var outgoingCapabilities: Set? = null,
) {
/**
@@ -40,7 +40,7 @@ class DeviceInfo(
try {
val encodedCertificate = Base64.encodeToString(certificate.encoded, 0)
- with (settings.edit()) {
+ with(settings.edit()) {
putString("certificate", encodedCertificate)
putString("deviceName", name)
putString("deviceType", type.toString())
@@ -73,7 +73,7 @@ class DeviceInfo(
*/
@JvmStatic
@Throws(CertificateException::class)
- fun loadFromSettings(context : Context, deviceId: String, settings: SharedPreferences) =
+ fun loadFromSettings(context: Context, deviceId: String, settings: SharedPreferences) =
with(settings) {
DeviceInfo(
id = deviceId,
@@ -104,8 +104,8 @@ class DeviceInfo(
@JvmStatic
fun isValidIdentityPacket(identityPacket: NetworkPacket): Boolean = with(identityPacket) {
type == NetworkPacket.PACKET_TYPE_IDENTITY &&
- DeviceHelper.filterName(getString("deviceName", "")).isNotBlank() &&
- getString("deviceId", "").isNotBlank()
+ DeviceHelper.filterName(getString("deviceName", "")).isNotBlank() &&
+ getString("deviceId", "").isNotBlank()
}
}
}
@@ -126,7 +126,7 @@ enum class DeviceType {
ContextCompat.getDrawable(context, toDrawableId())!!
@DrawableRes
- private fun toDrawableId() =
+ fun toDrawableId() =
when (this) {
PHONE -> R.drawable.ic_device_phone_32dp
TABLET -> R.drawable.ic_device_tablet_32dp
@@ -135,6 +135,15 @@ enum class DeviceType {
else -> R.drawable.ic_device_desktop_32dp
}
+ fun toShortcutDrawableId() =
+ when (this) {
+ PHONE -> R.drawable.ic_device_phone_shortcut
+ TABLET -> R.drawable.ic_device_tablet_shortcut
+ TV -> R.drawable.ic_device_tv_shortcut
+ LAPTOP -> R.drawable.ic_device_laptop_shortcut
+ else -> R.drawable.ic_device_desktop_shortcut
+ }
+
companion object {
@JvmStatic
fun fromString(s: String) =
diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareActivity.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareActivity.java
index f8dc761f..188fa354 100644
--- a/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareActivity.java
+++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareActivity.java
@@ -8,6 +8,7 @@ package org.kde.kdeconnect.Plugins.SharePlugin;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.os.Build;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
@@ -160,7 +161,10 @@ public class ShareActivity extends AppCompatActivity {
super.onStart();
final Intent intent = getIntent();
- final String deviceId = intent.getStringExtra("deviceId");
+ String deviceId = intent.getStringExtra("deviceId");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && deviceId == null) {
+ deviceId = intent.getStringExtra(Intent.EXTRA_SHORTCUT_ID);
+ }
if (deviceId != null) {
SharePlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, SharePlugin.class);
diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java
index 2f9c7aa5..a064979e 100644
--- a/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java
+++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java
@@ -24,6 +24,10 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.core.content.ContextCompat;
+import androidx.core.content.LocusIdCompat;
+import androidx.core.content.pm.ShortcutInfoCompat;
+import androidx.core.content.pm.ShortcutManagerCompat;
+import androidx.core.graphics.drawable.IconCompat;
import androidx.preference.PreferenceManager;
import org.apache.commons.lang3.ArrayUtils;
@@ -33,6 +37,7 @@ import org.kde.kdeconnect.Helpers.IntentHelper;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
+import org.kde.kdeconnect.UserInterface.MainActivity;
import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
import org.kde.kdeconnect.async.BackgroundJob;
import org.kde.kdeconnect.async.BackgroundJobHandler;
@@ -43,6 +48,7 @@ import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import java.util.Set;
/**
@@ -84,11 +90,34 @@ public class SharePlugin extends Plugin {
public boolean onCreate() {
super.onCreate();
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+ Intent shortcutIntent = new Intent(context, MainActivity.class);
+ shortcutIntent.setAction(Intent.ACTION_VIEW);
+ shortcutIntent.putExtra(MainActivity.EXTRA_DEVICE_ID, device.getDeviceId());
+
+ IconCompat icon = IconCompat.createWithResource(context, device.getDeviceType().toShortcutDrawableId());
+
+ ShortcutInfoCompat shortcut = new ShortcutInfoCompat
+ .Builder(context, device.getDeviceId())
+ .setIntent(shortcutIntent)
+ .setIcon(icon)
+ .setShortLabel(device.getName())
+ .setCategories(Set.of("org.kde.kdeconnect.category.SHARE_TARGET"))
+ .setLocusId(new LocusIdCompat(device.getDeviceId()))
+ .build();
+ ShortcutManagerCompat.pushDynamicShortcut(context, shortcut);
+
// Deliver URLs previously shared to this device now that it's connected
deliverPreviouslySentIntents();
return true;
}
+ @Override
+ public void onDestroy() {
+ ShortcutManagerCompat.removeLongLivedShortcuts(context, List.of(device.getDeviceId()));
+ super.onDestroy();
+ }
+
private void deliverPreviouslySentIntents() {
Set currentUrlSet = mSharedPrefs.getStringSet(KEY_UNREACHABLE_URL_LIST + device.getDeviceId(), null);
if (currentUrlSet != null) {