mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 09:58:08 +00:00
Add logs reading for sending clipboard on Android 10
Enable with: adb -d shell pm grant org.kde.kdeconnect_tp android.permission.READ_LOGS; adb -d shell appops set org.kde.kdeconnect_tp SYSTEM_ALERT_WINDOW allow; adb -d shell am force-stop org.kde.kdeconnect_tp;
This commit is contained in:
parent
de1e68d62f
commit
edc655da5a
@ -44,6 +44,8 @@
|
|||||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.READ_LOGS" tools:ignore="ProtectedPermissions" />
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
package org.kde.kdeconnect;
|
package org.kde.kdeconnect;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
@ -14,6 +15,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@ -353,10 +355,9 @@ public class BackgroundService extends Service {
|
|||||||
notification.setContentText(getString(R.string.foreground_notification_devices, TextUtils.join(", ", connectedDevices)));
|
notification.setContentText(getString(R.string.foreground_notification_devices, TextUtils.join(", ", connectedDevices)));
|
||||||
|
|
||||||
// Adding an action button to send clipboard manually in Android 10 and later.
|
// Adding an action button to send clipboard manually in Android 10 and later.
|
||||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P &&
|
||||||
Intent sendClipboard = new Intent(this, ClipboardFloatingActivity.class);
|
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_LOGS) == PackageManager.PERMISSION_DENIED) {
|
||||||
sendClipboard.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
|
Intent sendClipboard = ClipboardFloatingActivity.getIntent(this, true);
|
||||||
sendClipboard.putExtra("connectedDeviceIds", connectedDeviceIds);
|
|
||||||
PendingIntent sendPendingClipboard = PendingIntent.getActivity(this, 3, sendClipboard, PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent sendPendingClipboard = PendingIntent.getActivity(this, 3, sendClipboard, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
notification.addAction(0, getString(R.string.foreground_notification_send_clipboard), sendPendingClipboard);
|
notification.addAction(0, getString(R.string.foreground_notification_send_clipboard), sendPendingClipboard);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2020 Anjani Kumar <anjanik012@gmail.com>
|
* SPDX-FileCopyrightText: 2020 Anjani Kumar <anjanik012@gmail.com>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Ilmaz Gumerov <ilmaz1309@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
* 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.ClipData;
|
||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
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 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.
|
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 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
|
https://developer.android.com/reference/android/Manifest.permission#READ_LOGS
|
||||||
This permission can be gained by only from the adb by the user.
|
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/
|
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.
|
Currently this activity is bering triggered from a button in Foreground Notification or quick settings tile.
|
||||||
* */
|
* */
|
||||||
public class ClipboardFloatingActivity extends AppCompatActivity {
|
public class ClipboardFloatingActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private ArrayList<Device> 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
|
@Override
|
||||||
public void onWindowFocusChanged(boolean hasFocus) {
|
public void onWindowFocusChanged(boolean hasFocus) {
|
||||||
super.onWindowFocusChanged(hasFocus);
|
super.onWindowFocusChanged(hasFocus);
|
||||||
if (hasFocus) {
|
if (hasFocus) {
|
||||||
// We are now sure that clipboard can be accessed from here.
|
// We are now sure that clipboard can be accessed from here.
|
||||||
ClipboardManager clipboardManager = ContextCompat.getSystemService(this,
|
ClipboardListener.instance(this).onClipboardChanged();
|
||||||
ClipboardManager.class);
|
if (shouldShowToast()) {
|
||||||
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();
|
Toast.makeText(this, R.string.pref_plugin_clipboard_sent, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
finish();
|
finish();
|
||||||
@ -70,12 +73,10 @@ public class ClipboardFloatingActivity extends AppCompatActivity {
|
|||||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
|
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
|
||||||
|
|
||||||
getWindow().setAttributes(wlp);
|
getWindow().setAttributes(wlp);
|
||||||
ArrayList<String> connectedDeviceIds = getIntent().getStringArrayListExtra("connectedDeviceIds");
|
}
|
||||||
if (connectedDeviceIds != null) {
|
|
||||||
for (String deviceId : connectedDeviceIds) {
|
private boolean shouldShowToast() {
|
||||||
connectedDevices.add(BackgroundService.getInstance().getDevice(deviceId));
|
return getIntent().getBooleanExtra(KEY_SHOW_TOAST, false);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,22 +1,32 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Ilmaz Gumerov <ilmaz1309@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
|
package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
import androidx.core.content.ContextCompat;
|
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.HashSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
public class ClipboardListener {
|
public class ClipboardListener {
|
||||||
@ -32,7 +42,6 @@ public class ClipboardListener {
|
|||||||
private long updateTimestamp;
|
private long updateTimestamp;
|
||||||
|
|
||||||
private ClipboardManager cm = null;
|
private ClipboardManager cm = null;
|
||||||
private ClipboardManager.OnPrimaryClipChangedListener listener;
|
|
||||||
|
|
||||||
private static ClipboardListener _instance = null;
|
private static ClipboardListener _instance = null;
|
||||||
|
|
||||||
@ -57,28 +66,51 @@ public class ClipboardListener {
|
|||||||
|
|
||||||
new Handler(Looper.getMainLooper()).post(() -> {
|
new Handler(Looper.getMainLooper()).post(() -> {
|
||||||
cm = ContextCompat.getSystemService(context, ClipboardManager.class);
|
cm = ContextCompat.getSystemService(context, ClipboardManager.class);
|
||||||
listener = () -> {
|
cm.addPrimaryClipChangedListener(this::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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
cm.addPrimaryClipChangedListener(listener);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
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() {
|
public String getCurrentContent() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user