mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-04-21 01:33:36 +00:00
Merge 25d5778d6f into cbc5917050
This commit is contained in:
commit
7299bd6c4f
2 changed files with 113 additions and 0 deletions
|
|
@ -13,6 +13,7 @@ import com.genymobile.scrcpy.opengl.OpenGLRunner;
|
|||
import com.genymobile.scrcpy.util.AffineMatrix;
|
||||
import com.genymobile.scrcpy.util.Ln;
|
||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||
import com.genymobile.scrcpy.wrappers.WindowManager;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.display.VirtualDisplay;
|
||||
|
|
@ -199,6 +200,10 @@ public class NewDisplayCapture extends SurfaceCapture {
|
|||
ServiceManager.getWindowManager().setDisplayImePolicy(virtualDisplayId, displayImePolicy);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= AndroidVersions.API_34_ANDROID_14) {
|
||||
ServiceManager.getWindowManager().setDisplayWindowingMode(virtualDisplayId, WindowManager.WINDOWING_MODE_FREEFORM);
|
||||
}
|
||||
|
||||
displaySizeMonitor.start(virtualDisplayId, this::invalidate);
|
||||
} catch (Exception e) {
|
||||
Ln.e("Could not create display", e);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,15 @@ import com.genymobile.scrcpy.AndroidVersions;
|
|||
import com.genymobile.scrcpy.util.Ln;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.view.IDisplayWindowListener;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.List;
|
||||
|
||||
public final class WindowManager {
|
||||
|
||||
|
|
@ -18,6 +22,12 @@ public final class WindowManager {
|
|||
public static final int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1;
|
||||
public static final int DISPLAY_IME_POLICY_HIDE = 2;
|
||||
|
||||
// android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
|
||||
public static final int WINDOWING_MODE_FREEFORM = 5;
|
||||
|
||||
// android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER
|
||||
private static final int FEATURE_DEFAULT_TASK_CONTAINER = 1;
|
||||
|
||||
private final IInterface manager;
|
||||
private Method getRotationMethod;
|
||||
|
||||
|
|
@ -264,4 +274,102 @@ public final class WindowManager {
|
|||
Ln.e("Could not invoke method", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the windowing mode of a display's default task area via WindowContainerTransaction.
|
||||
*
|
||||
* This is required for Android desktop mode to operate in freeform windowing mode on virtual displays.
|
||||
*/
|
||||
@TargetApi(AndroidVersions.API_34_ANDROID_14)
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setDisplayWindowingMode(int displayId, int windowingMode) {
|
||||
// A Binder token with the correct AIDL interface descriptor, so that any system server
|
||||
// callbacks during registration are properly identified and silently handled
|
||||
IBinder organizerBinder = new Binder() {
|
||||
{
|
||||
attachInterface(null, "android.window.IDisplayAreaOrganizer");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
|
||||
throws android.os.RemoteException {
|
||||
if (super.onTransact(code, data, reply, flags)) {
|
||||
return true;
|
||||
}
|
||||
// Silently accept any organizer callback transactions
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Object organizerProxy = null;
|
||||
Object daoController = null;
|
||||
try {
|
||||
// Get the IWindowOrganizerController via ServiceManager
|
||||
IInterface windowOrganizerController = ServiceManager.getService("window_organizer",
|
||||
"android.window.IWindowOrganizerController");
|
||||
if (windowOrganizerController == null) {
|
||||
Ln.w("window_organizer service not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the IDisplayAreaOrganizerController
|
||||
daoController = windowOrganizerController.getClass().getMethod("getDisplayAreaOrganizerController").invoke(windowOrganizerController);
|
||||
|
||||
// Create a no-op IDisplayAreaOrganizer proxy for registration; callbacks are ignored since we unregister immediately
|
||||
Class<?> idaoClass = Class.forName("android.window.IDisplayAreaOrganizer");
|
||||
organizerProxy = Proxy.newProxyInstance(
|
||||
ClassLoader.getSystemClassLoader(),
|
||||
new Class[]{idaoClass},
|
||||
(proxy, method, args) -> {
|
||||
if ("asBinder".equals(method.getName())) {
|
||||
return organizerBinder;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
// Register the organizer to get display area tokens
|
||||
Object parceledList = daoController.getClass()
|
||||
.getMethod("registerOrganizer", idaoClass, int.class)
|
||||
.invoke(daoController, organizerProxy, FEATURE_DEFAULT_TASK_CONTAINER);
|
||||
|
||||
List<Object> displayAreaInfos = (List<Object>) parceledList.getClass().getMethod("getList").invoke(parceledList);
|
||||
|
||||
// Find the display area token matching our virtual display
|
||||
Object targetToken = null;
|
||||
for (Object info : displayAreaInfos) {
|
||||
Object displayAreaInfo = info.getClass().getMethod("getDisplayAreaInfo").invoke(info);
|
||||
int daDisplayId = displayAreaInfo.getClass().getDeclaredField("displayId").getInt(displayAreaInfo);
|
||||
if (daDisplayId == displayId) {
|
||||
targetToken = displayAreaInfo.getClass().getDeclaredField("token").get(displayAreaInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetToken != null) {
|
||||
// Create a WindowContainerTransaction to set the windowing mode
|
||||
Class<?> wctClass = Class.forName("android.window.WindowContainerTransaction");
|
||||
Object wct = wctClass.getDeclaredConstructor().newInstance();
|
||||
|
||||
Class<?> tokenClass = Class.forName("android.window.WindowContainerToken");
|
||||
wctClass.getMethod("setWindowingMode", tokenClass, int.class).invoke(wct, targetToken, windowingMode);
|
||||
|
||||
// Apply the transaction
|
||||
windowOrganizerController.getClass().getMethod("applyTransaction", wctClass).invoke(windowOrganizerController, wct);
|
||||
} else {
|
||||
Ln.w("Could not find display area for display " + displayId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Ln.w("Could not set windowing mode for display " + displayId, e);
|
||||
} finally {
|
||||
// Always unregister the organizer
|
||||
if (organizerProxy != null && daoController != null) {
|
||||
try {
|
||||
Class<?> idaoClass = Class.forName("android.window.IDisplayAreaOrganizer");
|
||||
daoController.getClass().getMethod("unregisterOrganizer", idaoClass).invoke(daoController, organizerProxy);
|
||||
} catch (Exception e) {
|
||||
Ln.w("Could not unregister display area organizer", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue