diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 46a58a5c..70471ac8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -44,6 +44,8 @@
+
+
diff --git a/src/org/kde/kdeconnect/BackgroundService.java b/src/org/kde/kdeconnect/BackgroundService.java
index efb7a58a..e08c3516 100644
--- a/src/org/kde/kdeconnect/BackgroundService.java
+++ b/src/org/kde/kdeconnect/BackgroundService.java
@@ -6,6 +6,7 @@
package org.kde.kdeconnect;
+import android.Manifest;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -14,6 +15,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.os.Binder;
import android.os.Build;
@@ -353,10 +355,9 @@ public class BackgroundService extends Service {
notification.setContentText(getString(R.string.foreground_notification_devices, TextUtils.join(", ", connectedDevices)));
// 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);
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P &&
+ ContextCompat.checkSelfPermission(this, Manifest.permission.READ_LOGS) == PackageManager.PERMISSION_DENIED) {
+ Intent sendClipboard = ClipboardFloatingActivity.getIntent(this, true);
PendingIntent sendPendingClipboard = PendingIntent.getActivity(this, 3, sendClipboard, PendingIntent.FLAG_UPDATE_CURRENT);
notification.addAction(0, getString(R.string.foreground_notification_send_clipboard), sendPendingClipboard);
}
diff --git a/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardFloatingActivity.java b/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardFloatingActivity.java
index 776ec983..bafae7fa 100644
--- a/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardFloatingActivity.java
+++ b/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardFloatingActivity.java
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Anjani Kumar
+ * SPDX-FileCopyrightText: 2021 Ilmaz Gumerov
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
@@ -8,19 +9,16 @@ package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
import android.content.ClipData;
import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.core.content.ContextCompat;
-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.
@@ -30,30 +28,35 @@ import java.util.ArrayList;
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/
+ Like:
+ # Enable the READ_LOGS permission. There is no other way to do this for a regular user app.
+ adb -d shell pm grant org.kde.kdeconnect_tp android.permission.READ_LOGS;
+ # Allow "Drawing over other apps", also accessible from Settings on the phone.
+ # Optional, but makes the feature much more reliable.
+ adb -d shell appops set org.kde.kdeconnect_tp SYSTEM_ALERT_WINDOW allow;
+ # Kill the app, new permissions take effect on restart.
+ adb -d shell am force-stop org.kde.kdeconnect_tp;
Currently this activity is bering triggered from a button in Foreground Notification or quick settings tile.
* */
public class ClipboardFloatingActivity extends AppCompatActivity {
- private ArrayList connectedDevices = new ArrayList<>();
+ private static final String KEY_SHOW_TOAST = "SHOW_TOAST";
+
+ public static Intent getIntent(Context context, boolean showToast) {
+ Intent startIntent = new Intent(context.getApplicationContext(), ClipboardFloatingActivity.class);
+ startIntent.putExtra(KEY_SHOW_TOAST, showToast);
+ startIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+ return startIntent;
+ }
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
// We are now sure that clipboard can be accessed from here.
- ClipboardManager clipboardManager = ContextCompat.getSystemService(this,
- ClipboardManager.class);
- 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);
- }
- }
+ ClipboardListener.instance(this).onClipboardChanged();
+ if (shouldShowToast()) {
Toast.makeText(this, R.string.pref_plugin_clipboard_sent, Toast.LENGTH_SHORT).show();
}
finish();
@@ -70,12 +73,10 @@ public class ClipboardFloatingActivity extends AppCompatActivity {
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));
- }
- }
+ }
+
+ private boolean shouldShowToast() {
+ return getIntent().getBooleanExtra(KEY_SHOW_TOAST, false);
}
}
diff --git a/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardListener.java b/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardListener.java
index fb1944b3..ea9a1321 100644
--- a/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardListener.java
+++ b/src/org/kde/kdeconnect/Plugins/ClibpoardPlugin/ClipboardListener.java
@@ -1,22 +1,32 @@
/*
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora
+ * SPDX-FileCopyrightText: 2021 Ilmaz Gumerov
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
+import android.Manifest;
import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.core.content.ContextCompat;
+import org.kde.kdeconnect_tp.BuildConfig;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.HashSet;
+import java.util.Locale;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class ClipboardListener {
@@ -32,7 +42,6 @@ public class ClipboardListener {
private long updateTimestamp;
private ClipboardManager cm = null;
- private ClipboardManager.OnPrimaryClipChangedListener listener;
private static ClipboardListener _instance = null;
@@ -57,28 +66,51 @@ public class ClipboardListener {
new Handler(Looper.getMainLooper()).post(() -> {
cm = ContextCompat.getSystemService(context, ClipboardManager.class);
- listener = () -> {
- try {
-
- ClipData.Item item = cm.getPrimaryClip().getItemAt(0);
- String content = item.coerceToText(context).toString();
-
- if (content.equals(currentContent)) {
- return;
- }
- updateTimestamp = System.currentTimeMillis();
- currentContent = content;
-
- for (ClipboardObserver observer : observers) {
- observer.clipboardChanged(content);
- }
-
- } catch (Exception e) {
- //Probably clipboard was not text
- }
- };
- cm.addPrimaryClipChangedListener(listener);
+ cm.addPrimaryClipChangedListener(this::onClipboardChanged);
});
+
+
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P &&
+ ContextCompat.checkSelfPermission(context, Manifest.permission.READ_LOGS) == PackageManager.PERMISSION_GRANTED) {
+ new Thread(() -> {
+ try {
+ String timeStamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US).format(new Date());
+ // Listen only ClipboardService errors after now
+ Process process = Runtime.getRuntime().exec(new String[]{"logcat", "-T", timeStamp, "ClipboardService:E", "*:S"});
+ BufferedReader bufferedReader = new BufferedReader(
+ new InputStreamReader(
+ process.getInputStream()
+ )
+ );
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ if (line.contains(BuildConfig.APPLICATION_ID)) {
+ context.startActivity(ClipboardFloatingActivity.getIntent(context, false));
+ }
+ }
+ } catch (Exception ignored) {
+ }
+ }).start();
+ }
+ }
+
+ public void onClipboardChanged() {
+ try {
+ ClipData.Item item = cm.getPrimaryClip().getItemAt(0);
+ String content = item.coerceToText(context).toString();
+
+ if (content.equals(currentContent)) {
+ return;
+ }
+ updateTimestamp = System.currentTimeMillis();
+ currentContent = content;
+
+ for (ClipboardObserver observer : observers) {
+ observer.clipboardChanged(content);
+ }
+ } catch (Exception e) {
+ //Probably clipboard was not text
+ }
}
public String getCurrentContent() {