Restart capture when screen resolution changes

This commit is contained in:
Simon Chan 2023-11-27 17:11:08 +08:00 committed by Romain Vimont
parent 762ed2755a
commit 391f2de3fa
3 changed files with 91 additions and 1 deletions

View file

@ -2,6 +2,7 @@ package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.ClipboardManager;
import com.genymobile.scrcpy.wrappers.DisplayControl;
import com.genymobile.scrcpy.wrappers.DisplayManager;
import com.genymobile.scrcpy.wrappers.InputManager;
import com.genymobile.scrcpy.wrappers.ServiceManager;
import com.genymobile.scrcpy.wrappers.SurfaceControl;
@ -10,6 +11,8 @@ import com.genymobile.scrcpy.wrappers.WindowManager;
import android.content.IOnPrimaryClipChangedListener;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.SystemClock;
import android.view.IDisplayFoldListener;
@ -33,6 +36,10 @@ public final class Device {
public static final int LOCK_VIDEO_ORIENTATION_UNLOCKED = -1;
public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2;
public interface DisplayChangeListener {
void onDisplayChanged();
}
public interface RotationListener {
void onRotationChanged(int rotation);
}
@ -51,6 +58,7 @@ public final class Device {
private Size deviceSize;
private ScreenInfo screenInfo;
private DisplayChangeListener displayChangeListener;
private RotationListener rotationListener;
private FoldListener foldListener;
private ClipboardListener clipboardListener;
@ -86,6 +94,27 @@ public final class Device {
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
layerStack = displayInfo.getLayerStack();
HandlerThread displayListenerThread = new HandlerThread("DisplayListenerThread");
displayListenerThread.start();
Handler displayListenerHandler = new Handler(displayListenerThread.getLooper());
ServiceManager.getDisplayManager().registerDisplayListener(new DisplayManager.DisplayListener() {
@Override
public void onDisplayChanged(int displayId) {
if (Device.this.displayId != displayId) {
return;
}
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
deviceSize = displayInfo.getSize();
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
if (displayChangeListener != null) {
displayChangeListener.onDisplayChanged();
}
}
}, displayListenerHandler);
ServiceManager.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() {
@Override
public void onRotationChanged(int rotation) {
@ -253,6 +282,10 @@ public final class Device {
return ServiceManager.getPowerManager().isScreenOn();
}
public synchronized void setDisplayChangeListener(DisplayChangeListener displayChangeListener) {
this.displayChangeListener = displayChangeListener;
}
public synchronized void setRotationListener(RotationListener rotationListener) {
this.rotationListener = rotationListener;
}

View file

@ -7,7 +7,7 @@ import android.os.Build;
import android.os.IBinder;
import android.view.Surface;
public class ScreenCapture extends SurfaceCapture implements Device.RotationListener, Device.FoldListener {
public class ScreenCapture extends SurfaceCapture implements Device.DisplayChangeListener, Device.RotationListener, Device.FoldListener {
private final Device device;
private IBinder display;
@ -18,6 +18,7 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
@Override
public void init() {
device.setDisplayChangeListener(this);
device.setRotationListener(this);
device.setFoldListener(this);
}
@ -41,6 +42,7 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
@Override
public void release() {
device.setDisplayChangeListener(null);
device.setRotationListener(null);
device.setFoldListener(null);
if (display != null) {
@ -69,6 +71,11 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
requestReset();
}
@Override
public void onDisplayChanged() {
requestReset();
}
private static IBinder createDisplay() {
// Since Android 12 (preview), secure displays could not be created with shell permissions anymore.
// On Android 12 preview, SDK_INT is still R (not S), but CODENAME is "S".

View file

@ -5,13 +5,20 @@ import com.genymobile.scrcpy.DisplayInfo;
import com.genymobile.scrcpy.Ln;
import com.genymobile.scrcpy.Size;
import android.os.Build;
import android.os.Handler;
import android.view.Display;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class DisplayManager {
// Constant copied from AOSP framework_base: core/java/android/hardware/display/DisplayManager.java
private static final long EVENT_FLAG_DISPLAY_CHANGED = 1L << 2;
private final Object manager; // instance of hidden class android.hardware.display.DisplayManagerGlobal
public DisplayManager(Object manager) {
@ -94,4 +101,47 @@ public final class DisplayManager {
throw new AssertionError(e);
}
}
public void registerDisplayListener(DisplayListener listener, Handler handler) {
try {
Class<?> displayListenerClass = Class.forName("android.hardware.display.DisplayManager$DisplayListener");
Object displayListenerProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{displayListenerClass},
(proxy, method, args) -> {
if ("onDisplayChanged".equals(method.getName())) {
listener.onDisplayChanged((int) args[0]);
}
return null;
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
try {
manager.getClass()
.getMethod("registerDisplayListener", displayListenerClass, Handler.class, long.class)
.invoke(manager, displayListenerProxy, handler, EVENT_FLAG_DISPLAY_CHANGED);
return;
} catch (NoSuchMethodException e) {
// fall-through
}
}
manager.getClass()
.getMethod("registerDisplayListener", displayListenerClass, Handler.class)
.invoke(manager, displayListenerProxy, handler);
} catch (Exception e) {
// Screen size won't be updated, not a fatal error
Ln.e("Could not register display listener", e);
}
}
// Partially copied from AOSP framework_base: core/java/android/hardware/display/DisplayManager.java
public interface DisplayListener {
/**
* Called whenever the properties of a logical {@link android.view.Display},
* such as size and density, have changed.
*
* @param displayId The id of the logical display that changed.
*/
void onDisplayChanged(int displayId);
}
}