File indexing completed on 2024-12-22 04:41:38
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 package org.kde.kdeconnect.Plugins.ClibpoardPlugin; 0008 0009 0010 import android.Manifest; 0011 import android.app.Activity; 0012 import android.content.ClipData; 0013 import android.content.ClipboardManager; 0014 import android.content.Context; 0015 import android.content.pm.PackageManager; 0016 import android.os.Build; 0017 import android.widget.Toast; 0018 0019 import androidx.annotation.DrawableRes; 0020 import androidx.annotation.NonNull; 0021 import androidx.core.content.ContextCompat; 0022 0023 import org.kde.kdeconnect.NetworkPacket; 0024 import org.kde.kdeconnect.Plugins.Plugin; 0025 import org.kde.kdeconnect.Plugins.PluginFactory; 0026 import org.kde.kdeconnect_tp.R; 0027 0028 @PluginFactory.LoadablePlugin 0029 public class ClipboardPlugin extends Plugin { 0030 0031 /** 0032 * Packet containing just clipboard contents, sent when a device updates its clipboard. 0033 * <p> 0034 * The body should look like so: 0035 * { 0036 * "content": "password" 0037 * } 0038 */ 0039 private final static String PACKET_TYPE_CLIPBOARD = "kdeconnect.clipboard"; 0040 0041 /** 0042 * Packet containing clipboard contents and a timestamp that the contents were last updated, sent 0043 * on first connection 0044 * <p> 0045 * The timestamp is milliseconds since epoch. It can be 0, which indicates that the clipboard 0046 * update time is currently unknown. 0047 * <p> 0048 * The body should look like so: 0049 * { 0050 * "timestamp": 542904563213, 0051 * "content": "password" 0052 * } 0053 */ 0054 private final static String PACKET_TYPE_CLIPBOARD_CONNECT = "kdeconnect.clipboard.connect"; 0055 0056 @Override 0057 public @NonNull String getDisplayName() { 0058 return context.getResources().getString(R.string.pref_plugin_clipboard); 0059 } 0060 0061 @Override 0062 public @NonNull String getDescription() { 0063 return context.getResources().getString(R.string.pref_plugin_clipboard_desc); 0064 } 0065 0066 @Override 0067 public boolean onPacketReceived(@NonNull NetworkPacket np) { 0068 String content = np.getString("content"); 0069 switch (np.getType()) { 0070 case (PACKET_TYPE_CLIPBOARD): 0071 ClipboardListener.instance(context).setText(content); 0072 return true; 0073 case(PACKET_TYPE_CLIPBOARD_CONNECT): 0074 long packetTime = np.getLong("timestamp"); 0075 // If the packetTime is 0, it means the timestamp is unknown (so do nothing). 0076 if (packetTime == 0 || packetTime < ClipboardListener.instance(context).getUpdateTimestamp()) { 0077 return false; 0078 } 0079 0080 ClipboardListener.instance(context).setText(content); 0081 return true; 0082 } 0083 throw new UnsupportedOperationException("Unknown packet type: " + np.getType()); 0084 } 0085 0086 private final ClipboardListener.ClipboardObserver observer = this::propagateClipboard; 0087 0088 void propagateClipboard(String content) { 0089 NetworkPacket np = new NetworkPacket(ClipboardPlugin.PACKET_TYPE_CLIPBOARD); 0090 np.set("content", content); 0091 device.sendPacket(np); 0092 } 0093 0094 private void sendConnectPacket() { 0095 String content = ClipboardListener.instance(context).getCurrentContent(); 0096 NetworkPacket np = new NetworkPacket(ClipboardPlugin.PACKET_TYPE_CLIPBOARD_CONNECT); 0097 long timestamp = ClipboardListener.instance(context).getUpdateTimestamp(); 0098 np.set("timestamp", timestamp); 0099 np.set("content", content); 0100 device.sendPacket(np); 0101 } 0102 0103 0104 @Override 0105 public boolean onCreate() { 0106 ClipboardListener.instance(context).registerObserver(observer); 0107 sendConnectPacket(); 0108 return true; 0109 } 0110 0111 @Override 0112 public void onDestroy() { 0113 ClipboardListener.instance(context).removeObserver(observer); 0114 } 0115 0116 @Override 0117 public @NonNull String[] getSupportedPacketTypes() { 0118 return new String[]{PACKET_TYPE_CLIPBOARD, PACKET_TYPE_CLIPBOARD_CONNECT}; 0119 } 0120 0121 @Override 0122 public @NonNull String[] getOutgoingPacketTypes() { 0123 return new String[]{PACKET_TYPE_CLIPBOARD, PACKET_TYPE_CLIPBOARD_CONNECT}; 0124 } 0125 0126 @Override 0127 public @NonNull String getActionName() { 0128 return context.getString(R.string.send_clipboard); 0129 } 0130 0131 @Override 0132 public boolean displayAsButton(Context context) { 0133 return Build.VERSION.SDK_INT > Build.VERSION_CODES.P && 0134 ContextCompat.checkSelfPermission(context, Manifest.permission.READ_LOGS) == PackageManager.PERMISSION_DENIED; 0135 } 0136 0137 @Override 0138 public @DrawableRes int getIcon() { 0139 return R.drawable.ic_baseline_content_paste_24; 0140 } 0141 0142 @Override 0143 public void startMainActivity(Activity activity) { 0144 if (device != null) { 0145 ClipboardManager clipboardManager = ContextCompat.getSystemService(this.context, 0146 ClipboardManager.class); 0147 ClipData.Item item; 0148 if (clipboardManager.hasPrimaryClip()) { 0149 item = clipboardManager.getPrimaryClip().getItemAt(0); 0150 String content = item.coerceToText(this.context).toString(); 0151 this.propagateClipboard(content); 0152 Toast.makeText(this.context, R.string.pref_plugin_clipboard_sent, Toast.LENGTH_SHORT).show(); 0153 } 0154 } 0155 } 0156 0157 }