mirror of https://github.com/Genymobile/scrcpy
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
7.1 KiB
Java
208 lines
7.1 KiB
Java
package com.genymobile.scrcpy;
|
|
|
|
import com.genymobile.scrcpy.wrappers.InputManager;
|
|
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
|
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
|
import com.genymobile.scrcpy.wrappers.WindowManager;
|
|
|
|
import android.graphics.Rect;
|
|
import android.os.Build;
|
|
import android.os.IBinder;
|
|
import android.os.RemoteException;
|
|
import android.view.IRotationWatcher;
|
|
import android.view.InputEvent;
|
|
|
|
public final class Device {
|
|
|
|
public static final int POWER_MODE_OFF = SurfaceControl.POWER_MODE_OFF;
|
|
public static final int POWER_MODE_NORMAL = SurfaceControl.POWER_MODE_NORMAL;
|
|
|
|
public interface RotationListener {
|
|
void onRotationChanged(int rotation);
|
|
}
|
|
|
|
private final ServiceManager serviceManager = new ServiceManager();
|
|
|
|
private ScreenInfo screenInfo;
|
|
private RotationListener rotationListener;
|
|
|
|
/**
|
|
* Logical display identifier
|
|
*/
|
|
private final int displayId;
|
|
|
|
/**
|
|
* The surface flinger layer stack associated with this logical display
|
|
*/
|
|
private final int layerStack;
|
|
|
|
/**
|
|
* The FLAG_PRESENTATION from the DisplayInfo
|
|
*/
|
|
private final boolean isPresentationDisplay;
|
|
|
|
public Device(Options options) {
|
|
displayId = options.getDisplayId();
|
|
DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo(displayId);
|
|
if (displayInfo == null) {
|
|
int[] displayIds = serviceManager.getDisplayManager().getDisplayIds();
|
|
throw new InvalidDisplayIdException(displayId, displayIds);
|
|
}
|
|
|
|
int displayInfoFlags = displayInfo.getFlags();
|
|
|
|
screenInfo = ScreenInfo.computeScreenInfo(displayInfo, options.getCrop(), options.getMaxSize(), options.getLockedVideoOrientation());
|
|
layerStack = displayInfo.getLayerStack();
|
|
isPresentationDisplay = (displayInfoFlags & DisplayInfo.FLAG_PRESENTATION) != 0;
|
|
|
|
registerRotationWatcher(new IRotationWatcher.Stub() {
|
|
@Override
|
|
public void onRotationChanged(int rotation) throws RemoteException {
|
|
synchronized (Device.this) {
|
|
screenInfo = screenInfo.withDeviceRotation(rotation);
|
|
|
|
// notify
|
|
if (rotationListener != null) {
|
|
rotationListener.onRotationChanged(rotation);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
if ((displayInfoFlags & DisplayInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS) == 0) {
|
|
Ln.w("Display doesn't have FLAG_SUPPORTS_PROTECTED_BUFFERS flag, mirroring can be restricted");
|
|
}
|
|
|
|
if (!supportsInputEvents()) {
|
|
Ln.w("Input events are not supported for displays with FLAG_PRESENTATION enabled for devices with API lower than 29");
|
|
}
|
|
}
|
|
|
|
public synchronized ScreenInfo getScreenInfo() {
|
|
return screenInfo;
|
|
}
|
|
|
|
public int getLayerStack() {
|
|
return layerStack;
|
|
}
|
|
|
|
public Point getPhysicalPoint(Position position) {
|
|
// it hides the field on purpose, to read it with a lock
|
|
@SuppressWarnings("checkstyle:HiddenField")
|
|
ScreenInfo screenInfo = getScreenInfo(); // read with synchronization
|
|
|
|
// ignore the locked video orientation, the events will apply in coordinates considered in the physical device orientation
|
|
Size unlockedVideoSize = screenInfo.getUnlockedVideoSize();
|
|
|
|
int reverseVideoRotation = screenInfo.getReverseVideoRotation();
|
|
// reverse the video rotation to apply the events
|
|
Position devicePosition = position.rotate(reverseVideoRotation);
|
|
|
|
Size clientVideoSize = devicePosition.getScreenSize();
|
|
if (!unlockedVideoSize.equals(clientVideoSize)) {
|
|
// The client sends a click relative to a video with wrong dimensions,
|
|
// the device may have been rotated since the event was generated, so ignore the event
|
|
return null;
|
|
}
|
|
Rect contentRect = screenInfo.getContentRect();
|
|
Point point = devicePosition.getPoint();
|
|
int convertedX = contentRect.left + point.getX() * contentRect.width() / unlockedVideoSize.getWidth();
|
|
int convertedY = contentRect.top + point.getY() * contentRect.height() / unlockedVideoSize.getHeight();
|
|
return new Point(convertedX, convertedY);
|
|
}
|
|
|
|
public static String getDeviceName() {
|
|
return Build.MODEL;
|
|
}
|
|
|
|
public boolean supportsInputEvents() {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
return true;
|
|
}
|
|
return !isPresentationDisplay;
|
|
}
|
|
|
|
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
|
if (!supportsInputEvents()) {
|
|
throw new AssertionError("Could not inject input event if !supportsInputEvents()");
|
|
}
|
|
|
|
if (displayId != 0 && !InputManager.setDisplayId(inputEvent, displayId)) {
|
|
return false;
|
|
}
|
|
|
|
return serviceManager.getInputManager().injectInputEvent(inputEvent, mode);
|
|
}
|
|
|
|
public boolean isScreenOn() {
|
|
return serviceManager.getPowerManager().isScreenOn();
|
|
}
|
|
|
|
public void registerRotationWatcher(IRotationWatcher rotationWatcher) {
|
|
serviceManager.getWindowManager().registerRotationWatcher(rotationWatcher);
|
|
}
|
|
|
|
public synchronized void setRotationListener(RotationListener rotationListener) {
|
|
this.rotationListener = rotationListener;
|
|
}
|
|
|
|
public void expandNotificationPanel() {
|
|
serviceManager.getStatusBarManager().expandNotificationsPanel();
|
|
}
|
|
|
|
public void collapsePanels() {
|
|
serviceManager.getStatusBarManager().collapsePanels();
|
|
}
|
|
|
|
public String getClipboardText() {
|
|
CharSequence s = serviceManager.getClipboardManager().getText();
|
|
if (s == null) {
|
|
return null;
|
|
}
|
|
return s.toString();
|
|
}
|
|
|
|
public void setClipboardText(String text) {
|
|
boolean ok = serviceManager.getClipboardManager().setText(text);
|
|
if (ok) {
|
|
Ln.i("Device clipboard set");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param mode one of the {@code SCREEN_POWER_MODE_*} constants
|
|
*/
|
|
public void setScreenPowerMode(int mode) {
|
|
IBinder d = SurfaceControl.getBuiltInDisplay();
|
|
if (d == null) {
|
|
Ln.e("Could not get built-in display");
|
|
return;
|
|
}
|
|
boolean ok = SurfaceControl.setDisplayPowerMode(d, mode);
|
|
if (ok) {
|
|
Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on"));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Disable auto-rotation (if enabled), set the screen rotation and re-enable auto-rotation (if it was enabled).
|
|
*/
|
|
public void rotateDevice() {
|
|
WindowManager wm = serviceManager.getWindowManager();
|
|
|
|
boolean accelerometerRotation = !wm.isRotationFrozen();
|
|
|
|
int currentRotation = wm.getRotation();
|
|
int newRotation = (currentRotation & 1) ^ 1; // 0->1, 1->0, 2->1, 3->0
|
|
String newRotationString = newRotation == 0 ? "portrait" : "landscape";
|
|
|
|
Ln.i("Device rotation requested: " + newRotationString);
|
|
wm.freezeRotation(newRotation);
|
|
|
|
// restore auto-rotate if necessary
|
|
if (accelerometerRotation) {
|
|
wm.thawRotation();
|
|
}
|
|
}
|
|
}
|