Move clipboard management to Controller

Continue to declutter the global Device.

PR #5370 <https://github.com/Genymobile/scrcpy/pull/5370>
This commit is contained in:
Romain Vimont 2024-10-12 09:23:31 +02:00
parent 7cfefae5e1
commit d916429566
3 changed files with 34 additions and 58 deletions

View File

@ -9,7 +9,6 @@ import com.genymobile.scrcpy.audio.AudioRawRecorder;
import com.genymobile.scrcpy.audio.AudioSource; import com.genymobile.scrcpy.audio.AudioSource;
import com.genymobile.scrcpy.control.ControlChannel; import com.genymobile.scrcpy.control.ControlChannel;
import com.genymobile.scrcpy.control.Controller; import com.genymobile.scrcpy.control.Controller;
import com.genymobile.scrcpy.control.DeviceMessage;
import com.genymobile.scrcpy.device.ConfigurationException; import com.genymobile.scrcpy.device.ConfigurationException;
import com.genymobile.scrcpy.device.DesktopConnection; import com.genymobile.scrcpy.device.DesktopConnection;
import com.genymobile.scrcpy.device.Device; import com.genymobile.scrcpy.device.Device;
@ -142,7 +141,7 @@ public final class Server {
boolean sendDummyByte = options.getSendDummyByte(); boolean sendDummyByte = options.getSendDummyByte();
boolean camera = video && options.getVideoSource() == VideoSource.CAMERA; boolean camera = video && options.getVideoSource() == VideoSource.CAMERA;
final Device device = camera ? null : new Device(options); final Device device = camera ? null : new Device();
Workarounds.apply(); Workarounds.apply();
@ -158,10 +157,6 @@ public final class Server {
ControlChannel controlChannel = connection.getControlChannel(); ControlChannel controlChannel = connection.getControlChannel();
Controller controller = new Controller( Controller controller = new Controller(
device, options.getDisplayId(), controlChannel, cleanUp, options.getClipboardAutosync(), options.getPowerOn()); device, options.getDisplayId(), controlChannel, cleanUp, options.getClipboardAutosync(), options.getPowerOn());
device.setClipboardListener(text -> {
DeviceMessage msg = DeviceMessage.createClipboard(text);
controller.getSender().send(msg);
});
asyncProcessors.add(controller); asyncProcessors.add(controller);
} }

View File

@ -7,9 +7,11 @@ import com.genymobile.scrcpy.device.Device;
import com.genymobile.scrcpy.device.Point; import com.genymobile.scrcpy.device.Point;
import com.genymobile.scrcpy.device.Position; import com.genymobile.scrcpy.device.Position;
import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.wrappers.ClipboardManager;
import com.genymobile.scrcpy.wrappers.InputManager; import com.genymobile.scrcpy.wrappers.InputManager;
import com.genymobile.scrcpy.wrappers.ServiceManager; import com.genymobile.scrcpy.wrappers.ServiceManager;
import android.content.IOnPrimaryClipChangedListener;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.SystemClock; import android.os.SystemClock;
@ -23,6 +25,7 @@ import java.io.IOException;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class Controller implements AsyncProcessor { public class Controller implements AsyncProcessor {
@ -48,6 +51,8 @@ public class Controller implements AsyncProcessor {
private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
private final AtomicBoolean isSettingClipboard = new AtomicBoolean();
private long lastTouchDown; private long lastTouchDown;
private final PointersState pointersState = new PointersState(); private final PointersState pointersState = new PointersState();
private final MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[PointersState.MAX_POINTERS]; private final MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[PointersState.MAX_POINTERS];
@ -69,6 +74,29 @@ public class Controller implements AsyncProcessor {
if (!supportsInputEvents) { if (!supportsInputEvents) {
Ln.w("Input events are not supported for secondary displays before Android 10"); Ln.w("Input events are not supported for secondary displays before Android 10");
} }
if (clipboardAutosync) {
// If control and autosync are enabled, synchronize Android clipboard to the computer automatically
ClipboardManager clipboardManager = ServiceManager.getClipboardManager();
if (clipboardManager != null) {
clipboardManager.addPrimaryClipChangedListener(new IOnPrimaryClipChangedListener.Stub() {
@Override
public void dispatchPrimaryClipChanged() {
if (isSettingClipboard.get()) {
// This is a notification for the change we are currently applying, ignore it
return;
}
String text = Device.getClipboardText();
if (text != null) {
DeviceMessage msg = DeviceMessage.createClipboard(text);
sender.send(msg);
}
}
});
} else {
Ln.w("No clipboard manager, copy-paste between device and computer will not work");
}
}
} }
private UhidManager getUhidManager() { private UhidManager getUhidManager() {
@ -148,10 +176,6 @@ public class Controller implements AsyncProcessor {
sender.join(); sender.join();
} }
public DeviceMessageSender getSender() {
return sender;
}
private boolean handleEvent() throws IOException { private boolean handleEvent() throws IOException {
ControlMessage msg; ControlMessage msg;
try { try {
@ -452,7 +476,9 @@ public class Controller implements AsyncProcessor {
} }
private boolean setClipboard(String text, boolean paste, long sequence) { private boolean setClipboard(String text, boolean paste, long sequence) {
boolean ok = device.setClipboardText(text); isSettingClipboard.set(true);
boolean ok = Device.setClipboardText(text);
isSettingClipboard.set(false);
if (ok) { if (ok) {
Ln.i("Device clipboard set"); Ln.i("Device clipboard set");
} }

View File

@ -1,7 +1,6 @@
package com.genymobile.scrcpy.device; package com.genymobile.scrcpy.device;
import com.genymobile.scrcpy.AndroidVersions; import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.Options;
import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.video.ScreenInfo; import com.genymobile.scrcpy.video.ScreenInfo;
import com.genymobile.scrcpy.wrappers.ClipboardManager; import com.genymobile.scrcpy.wrappers.ClipboardManager;
@ -11,7 +10,6 @@ import com.genymobile.scrcpy.wrappers.ServiceManager;
import com.genymobile.scrcpy.wrappers.SurfaceControl; import com.genymobile.scrcpy.wrappers.SurfaceControl;
import com.genymobile.scrcpy.wrappers.WindowManager; import com.genymobile.scrcpy.wrappers.WindowManager;
import android.content.IOnPrimaryClipChangedListener;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
@ -21,7 +19,6 @@ import android.view.InputEvent;
import android.view.KeyCharacterMap; import android.view.KeyCharacterMap;
import android.view.KeyEvent; import android.view.KeyEvent;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
public final class Device { public final class Device {
@ -36,43 +33,8 @@ public final class Device {
public static final int LOCK_VIDEO_ORIENTATION_UNLOCKED = -1; public static final int LOCK_VIDEO_ORIENTATION_UNLOCKED = -1;
public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2; public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2;
public interface ClipboardListener {
void onClipboardTextChanged(String text);
}
private ClipboardListener clipboardListener;
private final AtomicBoolean isSettingClipboard = new AtomicBoolean();
private final AtomicReference<ScreenInfo> screenInfo = new AtomicReference<>(); // set by the ScreenCapture instance private final AtomicReference<ScreenInfo> screenInfo = new AtomicReference<>(); // set by the ScreenCapture instance
public Device(Options options) {
if (options.getControl() && options.getClipboardAutosync()) {
// If control and autosync are enabled, synchronize Android clipboard to the computer automatically
ClipboardManager clipboardManager = ServiceManager.getClipboardManager();
if (clipboardManager != null) {
clipboardManager.addPrimaryClipChangedListener(new IOnPrimaryClipChangedListener.Stub() {
@Override
public void dispatchPrimaryClipChanged() {
if (isSettingClipboard.get()) {
// This is a notification for the change we are currently applying, ignore it
return;
}
synchronized (Device.this) {
if (clipboardListener != null) {
String text = getClipboardText();
if (text != null) {
clipboardListener.onClipboardTextChanged(text);
}
}
}
}
});
} else {
Ln.w("No clipboard manager, copy-paste between device and computer will not work");
}
}
}
public Point getPhysicalPoint(Position position) { public Point getPhysicalPoint(Position position) {
// it hides the field on purpose, to read it with atomic access // it hides the field on purpose, to read it with atomic access
@SuppressWarnings("checkstyle:HiddenField") @SuppressWarnings("checkstyle:HiddenField")
@ -142,10 +104,6 @@ public final class Device {
return ServiceManager.getPowerManager().isScreenOn(); return ServiceManager.getPowerManager().isScreenOn();
} }
public synchronized void setClipboardListener(ClipboardListener clipboardListener) {
this.clipboardListener = clipboardListener;
}
public static void expandNotificationPanel() { public static void expandNotificationPanel() {
ServiceManager.getStatusBarManager().expandNotificationsPanel(); ServiceManager.getStatusBarManager().expandNotificationsPanel();
} }
@ -170,7 +128,7 @@ public final class Device {
return s.toString(); return s.toString();
} }
public boolean setClipboardText(String text) { public static boolean setClipboardText(String text) {
ClipboardManager clipboardManager = ServiceManager.getClipboardManager(); ClipboardManager clipboardManager = ServiceManager.getClipboardManager();
if (clipboardManager == null) { if (clipboardManager == null) {
return false; return false;
@ -185,10 +143,7 @@ public final class Device {
return false; return false;
} }
isSettingClipboard.set(true); return clipboardManager.setText(text);
boolean ok = clipboardManager.setText(text);
isSettingClipboard.set(false);
return ok;
} }
/** /**