diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eb8221f7..6416c0ca 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -135,6 +135,11 @@
+
+
KDE Connect
Not connected to any device
Connected to: %s
+ Send Clipboard
Telephony notifier
Send notifications for incoming calls
Battery report
@@ -17,6 +18,7 @@
Allows to browse this device\'s filesystem remotely
Clipboard sync
Share the clipboard content
+ Clipboard Sent
Remote input
Use your phone or tablet as a touchpad and keyboard
Slideshow remote
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 7faa68e4..b5585697 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -49,4 +49,12 @@
+
diff --git a/src/org/kde/kdeconnect/BackgroundService.java b/src/org/kde/kdeconnect/BackgroundService.java
index e878ff2c..8fe6b791 100644
--- a/src/org/kde/kdeconnect/BackgroundService.java
+++ b/src/org/kde/kdeconnect/BackgroundService.java
@@ -44,6 +44,7 @@ import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
+import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity;
@@ -346,10 +347,10 @@ public class BackgroundService extends Service {
}
ArrayList connectedDevices = new ArrayList<>();
- ArrayList deviceIds = new ArrayList<>();
+ ArrayList connectedDeviceIds = new ArrayList<>();
for (Device device : getDevices().values()) {
if (device.isReachable() && device.isPaired()) {
- deviceIds.add(device.getDeviceId());
+ connectedDeviceIds.add(device.getDeviceId());
connectedDevices.add(device.getName());
}
}
@@ -358,20 +359,30 @@ public class BackgroundService extends Service {
notification.setContentText(getString(R.string.foreground_notification_no_devices));
} else {
notification.setContentText(getString(R.string.foreground_notification_devices, TextUtils.join(", ", connectedDevices)));
- if (deviceIds.size() == 1) {
+
+ // Adding an action button to send clipboard manually in Android 10 and later.
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
+ Intent sendClipboard = new Intent(this, ClipboardFloatingActivity.class);
+ sendClipboard.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+ sendClipboard.putExtra("connectedDeviceIds", connectedDeviceIds);
+ PendingIntent sendPendingClipboard = PendingIntent.getActivity(this, 3, sendClipboard, PendingIntent.FLAG_UPDATE_CURRENT);
+ notification.addAction(0, getString(R.string.foreground_notification_send_clipboard), sendPendingClipboard);
+ }
+
+ if (connectedDeviceIds.size() == 1) {
// Adding two action buttons only when there is a single device connected.
// Setting up Send File Intent.
Intent sendFile = new Intent(this, SendFileActivity.class);
- sendFile.putExtra("deviceId", deviceIds.get(0));
+ sendFile.putExtra("deviceId", connectedDeviceIds.get(0));
PendingIntent sendPendingFile = PendingIntent.getActivity(this, 1, sendFile, PendingIntent.FLAG_UPDATE_CURRENT);
notification.addAction(0, getString(R.string.send_files), sendPendingFile);
// Checking if there are registered commands and adding the button.
- Device device = getDevice(deviceIds.get(0));
+ Device device = getDevice(connectedDeviceIds.get(0));
RunCommandPlugin plugin = (RunCommandPlugin) device.getPlugin("RunCommandPlugin");
if (plugin != null && !plugin.getCommandList().isEmpty()) {
Intent runCommand = new Intent(this, RunCommandActivity.class);
- runCommand.putExtra("deviceId", deviceIds.get(0));
+ runCommand.putExtra("deviceId", connectedDeviceIds.get(0));
PendingIntent runPendingCommand = PendingIntent.getActivity(this, 2, runCommand, PendingIntent.FLAG_UPDATE_CURRENT);
notification.addAction(0, getString(R.string.pref_plugin_runcommand), runPendingCommand);
}
diff --git a/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardFloatingActivity.java b/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardFloatingActivity.java
new file mode 100644
index 00000000..57e7abcf
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardFloatingActivity.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2020 Anjani Kumar
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+import org.kde.kdeconnect.BackgroundService;
+import org.kde.kdeconnect.Device;
+import org.kde.kdeconnect_tp.R;
+
+import java.util.ArrayList;
+
+/*
+ An activity to access the clipboard on Android 10 and later by raising over other apps.
+ This is invisible and doesn't require any interaction from the user.
+ This should be called when a change in clipboard is detected. This can be done by manually
+ when user wants to send the clipboard or by reading system log files which requires a special
+ privileged permission android.permission.READ_LOGS.
+ https://developer.android.com/reference/android/Manifest.permission#READ_LOGS
+ This permission can be gained by only from the adb by the user.
+ https://www.reddit.com/r/AndroidBusters/comments/fh60lt/how_to_solve_a_problem_with_the_clipboard_on/
+
+ Currently this activity is bering triggered from a button in Foreground Notification.
+* */
+public class ClipboardFloatingActivity extends AppCompatActivity {
+
+ private ArrayList connectedDevices = new ArrayList<>();
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (hasFocus) {
+ // We are now sure that clipboard can be accessed from here.
+ ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData.Item item;
+ if (clipboardManager.hasPrimaryClip()) {
+ item = clipboardManager.getPrimaryClip().getItemAt(0);
+ String content = item.coerceToText(this).toString();
+ for (Device device : connectedDevices) {
+ ClipboardPlugin clipboardPlugin = (ClipboardPlugin) device.getPlugin("ClipboardPlugin");
+ if (clipboardPlugin != null) {
+ clipboardPlugin.propagateClipboard(content);
+ }
+ }
+ Toast.makeText(this, R.string.pref_plugin_clipboard_sent, Toast.LENGTH_SHORT).show();
+ }
+ finish();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams wlp = getWindow().getAttributes();
+ wlp.dimAmount = 0;
+ wlp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+
+ getWindow().setAttributes(wlp);
+ ArrayList connectedDeviceIds = getIntent().getStringArrayListExtra("connectedDeviceIds");
+ if (connectedDeviceIds != null) {
+ for (String deviceId : connectedDeviceIds) {
+ connectedDevices.add(BackgroundService.getInstance().getDevice(deviceId));
+ }
+ }
+ }
+}
+
diff --git a/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardPlugin.java b/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardPlugin.java
index a0de5e03..54cae9ef 100644
--- a/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardPlugin.java
+++ b/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardPlugin.java
@@ -20,15 +20,11 @@
package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
-import android.os.Build;
-import androidx.fragment.app.DialogFragment;
-import androidx.preference.PreferenceManager;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
-import org.kde.kdeconnect.UserInterface.NoticeAlertDialogFragment;
import org.kde.kdeconnect_tp.R;
@PluginFactory.LoadablePlugin
@@ -59,8 +55,6 @@ public class ClipboardPlugin extends Plugin {
*/
private final static String PACKET_TYPE_CLIPBOARD_CONNECT = "kdeconnect.clipboard.connect";
- private final static String ANDROID_10_INCOMPAT_DIALOG_SHOWN_PREFERENCE = "android10IncompatDialogShown";
-
@Override
public String getDisplayName() {
return context.getResources().getString(R.string.pref_plugin_clipboard);
@@ -93,7 +87,7 @@ public class ClipboardPlugin extends Plugin {
private final ClipboardListener.ClipboardObserver observer = this::propagateClipboard;
- private void propagateClipboard(String content) {
+ void propagateClipboard(String content) {
NetworkPacket np = new NetworkPacket(ClipboardPlugin.PACKET_TYPE_CLIPBOARD);
np.set("content", content);
device.sendPacket(np);
@@ -108,26 +102,9 @@ public class ClipboardPlugin extends Plugin {
device.sendPacket(np);
}
- @Override
- public boolean checkRequiredPermissions() {
- return Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || PreferenceManager.getDefaultSharedPreferences(context).getBoolean(ANDROID_10_INCOMPAT_DIALOG_SHOWN_PREFERENCE, false);
- }
-
- @Override
- public DialogFragment getPermissionExplanationDialog() {
- PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(ANDROID_10_INCOMPAT_DIALOG_SHOWN_PREFERENCE, true).apply();
- return new NoticeAlertDialogFragment.Builder()
- .setTitle(R.string.pref_plugin_clipboard)
- .setMessage(R.string.clipboard_android_x_incompat)
- .setPositiveButton(R.string.sad_ok)
- .create();
- }
@Override
public boolean onCreate() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- return false;
- }
ClipboardListener.instance(context).registerObserver(observer);
sendConnectPacket();
return true;
diff --git a/src/org/kde/kdeconnect/UserInterface/MainActivity.java b/src/org/kde/kdeconnect/UserInterface/MainActivity.java
index ee862e08..55d3566f 100644
--- a/src/org/kde/kdeconnect/UserInterface/MainActivity.java
+++ b/src/org/kde/kdeconnect/UserInterface/MainActivity.java
@@ -378,12 +378,6 @@ public class MainActivity extends AppCompatActivity implements SharedPreferences
}
}
- public void reloadCurrentDevicePlugins() {
- BackgroundService.RunCommand(this, service -> {
- Device device = service.getDevice(mCurrentDevice);
- device.reloadPluginsFromSettings();
- });
- }
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
diff --git a/src/org/kde/kdeconnect/UserInterface/NoticeAlertDialogFragment.java b/src/org/kde/kdeconnect/UserInterface/NoticeAlertDialogFragment.java
deleted file mode 100644
index b0c40b6a..00000000
--- a/src/org/kde/kdeconnect/UserInterface/NoticeAlertDialogFragment.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2019 Erik Duisters
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License or (at your option) version 3 or any later version
- * accepted by the membership of KDE e.V. (or its successor approved
- * by the membership of KDE e.V.), which shall act as a proxy
- * defined in Section 14 of version 3 of the license.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.kde.kdeconnect.UserInterface;
-
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-
-import androidx.annotation.Nullable;
-import androidx.preference.PreferenceManager;
-
-import org.kde.kdeconnect_tp.R;
-
-public class NoticeAlertDialogFragment extends AlertDialogFragment {
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setCallback(new Callback() {
- @Override
- public void onPositiveButtonClicked() {
- //TODO: Find a way to pass this callback from the Builder. For now, this is only used in one place and this is the callback needed.
- MainActivity mainActivity = (MainActivity)requireActivity();
- mainActivity.reloadCurrentDevicePlugins();
- }
- });
- }
-
- public static class Builder extends AbstractBuilder {
-
- public Builder() {
- super();
- setTitle(R.string.pref_plugin_clipboard);
- setMessage(R.string.clipboard_android_x_incompat);
- setPositiveButton(R.string.sad_ok);
- }
-
- @Override
- public Builder getThis() {
- return this;
- }
-
- @Override
- protected NoticeAlertDialogFragment createFragment() {
- return new NoticeAlertDialogFragment();
- }
- }
-}