Loading src/com/android/launcher3/util/DisplayController.java +7 −3 Original line number Diff line number Diff line Loading @@ -242,7 +242,9 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { change |= CHANGE_SUPPORTED_BOUNDS; Point currentS = newInfo.currentSize; Point expectedS = oldInfo.mPerDisplayBounds.get(newInfo.displayId).first.size; Pair<CachedDisplayInfo, WindowBounds[]> cachedBounds = oldInfo.mPerDisplayBounds.get(newInfo.displayId); Point expectedS = cachedBounds == null ? null : cachedBounds.first.size; if (newInfo.supportedBounds.size() != oldInfo.supportedBounds.size()) { Log.e("b/198965093", "Inconsistent number of displays" Loading @@ -250,10 +252,12 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { + "\noldInfo.supportedBounds: " + oldInfo.supportedBounds + "\nnewInfo.supportedBounds: " + newInfo.supportedBounds); } if ((Math.min(currentS.x, currentS.y) != Math.min(expectedS.x, expectedS.y) if (expectedS != null && (Math.min(currentS.x, currentS.y) != Math.min(expectedS.x, expectedS.y) || Math.max(currentS.x, currentS.y) != Math.max(expectedS.x, expectedS.y)) && display.getState() == Display.STATE_OFF) { Log.e("b/198965093", "Display size changed while display is off, ignoring change"); Log.e("b/198965093", "Display size changed while display is off, ignoring change"); return; } } Loading src/com/android/launcher3/util/window/WindowManagerProxy.java +14 −8 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE; import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT; import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE; import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE; import static com.android.launcher3.ResourceUtils.getDimenByName; import static com.android.launcher3.Utilities.dpiFromPx; import static com.android.launcher3.util.MainThreadInitializedObject.forOverride; import static com.android.launcher3.util.RotationUtils.deltaRotation; Loading Loading @@ -157,16 +156,16 @@ public class WindowManagerProxy implements ResourceBasedOverride { int bottomNav = isTablet ? 0 : (config.screenHeightDp > config.screenWidthDp ? getDimenByName(NAVBAR_HEIGHT, systemRes, 0) ? getDimenByName(NAVBAR_HEIGHT, systemRes) : (isGesture ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0) ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes) : 0)); Insets newNavInsets = Insets.of(navInsets.left, navInsets.top, navInsets.right, bottomNav); insetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets); insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets); Insets statusBarInsets = oldInsets.getInsets(WindowInsets.Type.statusBars()); int statusBarHeight = getDimenByName("status_bar_height", systemRes, 0); int statusBarHeight = getDimenByName("status_bar_height", systemRes); Insets newStatusBarInsets = Insets.of( statusBarInsets.left, Math.max(statusBarInsets.top, statusBarHeight), Loading Loading @@ -222,23 +221,23 @@ public class WindowManagerProxy implements ResourceBasedOverride { boolean isTabletOrGesture = isTablet || (Utilities.ATLEAST_R && isGestureNav(context)); int statusBarHeight = getDimenByName("status_bar_height", systemRes, 0); int statusBarHeight = getDimenByName("status_bar_height", systemRes); int navBarHeightPortrait, navBarHeightLandscape, navbarWidthLandscape; navBarHeightPortrait = isTablet ? (mTaskbarDrawnInProcess ? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size)) : getDimenByName(NAVBAR_HEIGHT, systemRes, 0); : getDimenByName(NAVBAR_HEIGHT, systemRes); navBarHeightLandscape = isTablet ? (mTaskbarDrawnInProcess ? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size)) : (isTabletOrGesture ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0) : 0); ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes) : 0); navbarWidthLandscape = isTabletOrGesture ? 0 : getDimenByName(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, systemRes, 0); : getDimenByName(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, systemRes); WindowBounds[] result = new WindowBounds[4]; Point tempSize = new Point(); Loading Loading @@ -274,6 +273,13 @@ public class WindowManagerProxy implements ResourceBasedOverride { return result; } /** * Wrapper around the utility method for easier emulation */ protected int getDimenByName(String resName, Resources res) { return ResourceUtils.getDimenByName(resName, res, 0); } protected boolean isGestureNav(Context context) { return ResourceUtils.getIntegerByName("config_navBarInteractionMode", context.getResources(), INVALID_RESOURCE_HANDLE) == 2; Loading tests/res/raw/devices.json 0 → 100644 +45 −0 Original line number Diff line number Diff line { "pixel6pro": { "width": 1440, "height": 3120, "density": 560, "name": "pixel6pro", "cutout": "0, 130, 0, 0", "grids": [ "normal", "reasonable", "practical", "big", "crazy_big" ], "resourceOverrides": { "status_bar_height": 98, "navigation_bar_height_landscape": 56, "navigation_bar_height": 56, "navigation_bar_width": 56 } }, "test": { "data needs updating": 0 }, "pixel5": { "width": 1080, "height": 2340, "density": 440, "name": "pixel5", "cutout": "0, 136, 0, 0", "grids": [ "normal", "reasonable", "practical", "big", "crazy_big" ], "resourceOverrides": { "status_bar_height": 66, "navigation_bar_height_landscape": 44, "navigation_bar_height": 44, "navigation_bar_width": 44 } } } tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java 0 → 100644 +95 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.deviceemulator; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.net.Uri; import android.os.UserHandle; import android.view.Display; import android.view.IWindowManager; import android.view.WindowManagerGlobal; import androidx.test.uiautomator.UiDevice; import com.android.launcher3.deviceemulator.models.DeviceEmulationData; import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.util.window.WindowManagerProxy; import java.util.concurrent.Callable; public class DisplayEmulator { Context mContext; LauncherInstrumentation mLauncher; DisplayEmulator(Context context, LauncherInstrumentation launcher) { mContext = context; mLauncher = launcher; } /** * By changing the WindowManagerProxy we can override the window insets information **/ private IWindowManager changeWindowManagerInstance(DeviceEmulationData deviceData) { WindowManagerProxy.INSTANCE.initializeForTesting( new TestWindowManagerProxy(mContext, deviceData)); return WindowManagerGlobal.getWindowManagerService(); } public <T> T emulate(DeviceEmulationData device, String grid, Callable<T> runInEmulation) throws Exception { WindowManagerProxy original = WindowManagerProxy.INSTANCE.get(mContext); // Set up emulation final int userId = UserHandle.myUserId(); WindowManagerProxy.INSTANCE.initializeForTesting( new TestWindowManagerProxy(mContext, device)); IWindowManager wm = changeWindowManagerInstance(device); // Change density twice to force display controller to reset its state wm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, device.density / 2, userId); wm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, device.density, userId); wm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, device.width, device.height); wm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1); // Set up grid setGrid(grid); try { return runInEmulation.call(); } finally { // Clear emulation WindowManagerProxy.INSTANCE.initializeForTesting(original); UiDevice.getInstance(getInstrumentation()).executeShellCommand("cmd window reset"); } } private void setGrid(String gridType) { // When the grid changes, the desktop arrangement get stored in SQL and we need to wait to // make sure there is no SQL operations running and get SQL_BUSY error, that's why we need // to call mLauncher.waitForLauncherInitialized(); mLauncher.waitForLauncherInitialized(); String testProviderAuthority = mContext.getPackageName() + ".grid_control"; Uri gridUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(testProviderAuthority) .appendPath("default_grid") .build(); ContentValues values = new ContentValues(); values.put("name", gridType); mContext.getContentResolver().update(gridUri, values, null, null); } } tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.deviceemulator; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.view.Display; import android.view.WindowInsets; import com.android.launcher3.deviceemulator.models.DeviceEmulationData; import com.android.launcher3.util.RotationUtils; import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.window.CachedDisplayInfo; import com.android.launcher3.util.window.WindowManagerProxy; public class TestWindowManagerProxy extends WindowManagerProxy { private final DeviceEmulationData mDevice; public TestWindowManagerProxy(Context context, DeviceEmulationData device) { super(true); mDevice = device; } @Override public boolean isInternalDisplay(Display display) { return display.getDisplayId() == Display.DEFAULT_DISPLAY; } @Override protected int getDimenByName(String resName, Resources res) { Integer mock = mDevice.resourceOverrides.get(resName); return mock != null ? mock : super.getDimenByName(resName, res); } @Override public CachedDisplayInfo getDisplayInfo(Context context, Display display) { int rotation = display.getRotation(); Point size = new Point(mDevice.width, mDevice.height); RotationUtils.rotateSize(size, rotation); Rect cutout = new Rect(mDevice.cutout); RotationUtils.rotateRect(cutout, rotation); return new CachedDisplayInfo(getDisplayId(display), size, rotation, cutout); } @Override public WindowBounds getRealBounds(Context windowContext, Display display, CachedDisplayInfo info) { return estimateInternalDisplayBounds(windowContext) .get(getDisplayId(display)).second[display.getRotation()]; } @Override public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets, Rect outInsets) { outInsets.set(getRealBounds(context, context.getDisplay(), getDisplayInfo(context, context.getDisplay())).insets); return oldInsets; } } Loading
src/com/android/launcher3/util/DisplayController.java +7 −3 Original line number Diff line number Diff line Loading @@ -242,7 +242,9 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { change |= CHANGE_SUPPORTED_BOUNDS; Point currentS = newInfo.currentSize; Point expectedS = oldInfo.mPerDisplayBounds.get(newInfo.displayId).first.size; Pair<CachedDisplayInfo, WindowBounds[]> cachedBounds = oldInfo.mPerDisplayBounds.get(newInfo.displayId); Point expectedS = cachedBounds == null ? null : cachedBounds.first.size; if (newInfo.supportedBounds.size() != oldInfo.supportedBounds.size()) { Log.e("b/198965093", "Inconsistent number of displays" Loading @@ -250,10 +252,12 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { + "\noldInfo.supportedBounds: " + oldInfo.supportedBounds + "\nnewInfo.supportedBounds: " + newInfo.supportedBounds); } if ((Math.min(currentS.x, currentS.y) != Math.min(expectedS.x, expectedS.y) if (expectedS != null && (Math.min(currentS.x, currentS.y) != Math.min(expectedS.x, expectedS.y) || Math.max(currentS.x, currentS.y) != Math.max(expectedS.x, expectedS.y)) && display.getState() == Display.STATE_OFF) { Log.e("b/198965093", "Display size changed while display is off, ignoring change"); Log.e("b/198965093", "Display size changed while display is off, ignoring change"); return; } } Loading
src/com/android/launcher3/util/window/WindowManagerProxy.java +14 −8 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE; import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT; import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE; import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE; import static com.android.launcher3.ResourceUtils.getDimenByName; import static com.android.launcher3.Utilities.dpiFromPx; import static com.android.launcher3.util.MainThreadInitializedObject.forOverride; import static com.android.launcher3.util.RotationUtils.deltaRotation; Loading Loading @@ -157,16 +156,16 @@ public class WindowManagerProxy implements ResourceBasedOverride { int bottomNav = isTablet ? 0 : (config.screenHeightDp > config.screenWidthDp ? getDimenByName(NAVBAR_HEIGHT, systemRes, 0) ? getDimenByName(NAVBAR_HEIGHT, systemRes) : (isGesture ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0) ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes) : 0)); Insets newNavInsets = Insets.of(navInsets.left, navInsets.top, navInsets.right, bottomNav); insetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets); insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets); Insets statusBarInsets = oldInsets.getInsets(WindowInsets.Type.statusBars()); int statusBarHeight = getDimenByName("status_bar_height", systemRes, 0); int statusBarHeight = getDimenByName("status_bar_height", systemRes); Insets newStatusBarInsets = Insets.of( statusBarInsets.left, Math.max(statusBarInsets.top, statusBarHeight), Loading Loading @@ -222,23 +221,23 @@ public class WindowManagerProxy implements ResourceBasedOverride { boolean isTabletOrGesture = isTablet || (Utilities.ATLEAST_R && isGestureNav(context)); int statusBarHeight = getDimenByName("status_bar_height", systemRes, 0); int statusBarHeight = getDimenByName("status_bar_height", systemRes); int navBarHeightPortrait, navBarHeightLandscape, navbarWidthLandscape; navBarHeightPortrait = isTablet ? (mTaskbarDrawnInProcess ? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size)) : getDimenByName(NAVBAR_HEIGHT, systemRes, 0); : getDimenByName(NAVBAR_HEIGHT, systemRes); navBarHeightLandscape = isTablet ? (mTaskbarDrawnInProcess ? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size)) : (isTabletOrGesture ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0) : 0); ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes) : 0); navbarWidthLandscape = isTabletOrGesture ? 0 : getDimenByName(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, systemRes, 0); : getDimenByName(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, systemRes); WindowBounds[] result = new WindowBounds[4]; Point tempSize = new Point(); Loading Loading @@ -274,6 +273,13 @@ public class WindowManagerProxy implements ResourceBasedOverride { return result; } /** * Wrapper around the utility method for easier emulation */ protected int getDimenByName(String resName, Resources res) { return ResourceUtils.getDimenByName(resName, res, 0); } protected boolean isGestureNav(Context context) { return ResourceUtils.getIntegerByName("config_navBarInteractionMode", context.getResources(), INVALID_RESOURCE_HANDLE) == 2; Loading
tests/res/raw/devices.json 0 → 100644 +45 −0 Original line number Diff line number Diff line { "pixel6pro": { "width": 1440, "height": 3120, "density": 560, "name": "pixel6pro", "cutout": "0, 130, 0, 0", "grids": [ "normal", "reasonable", "practical", "big", "crazy_big" ], "resourceOverrides": { "status_bar_height": 98, "navigation_bar_height_landscape": 56, "navigation_bar_height": 56, "navigation_bar_width": 56 } }, "test": { "data needs updating": 0 }, "pixel5": { "width": 1080, "height": 2340, "density": 440, "name": "pixel5", "cutout": "0, 136, 0, 0", "grids": [ "normal", "reasonable", "practical", "big", "crazy_big" ], "resourceOverrides": { "status_bar_height": 66, "navigation_bar_height_landscape": 44, "navigation_bar_height": 44, "navigation_bar_width": 44 } } }
tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java 0 → 100644 +95 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.deviceemulator; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.net.Uri; import android.os.UserHandle; import android.view.Display; import android.view.IWindowManager; import android.view.WindowManagerGlobal; import androidx.test.uiautomator.UiDevice; import com.android.launcher3.deviceemulator.models.DeviceEmulationData; import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.util.window.WindowManagerProxy; import java.util.concurrent.Callable; public class DisplayEmulator { Context mContext; LauncherInstrumentation mLauncher; DisplayEmulator(Context context, LauncherInstrumentation launcher) { mContext = context; mLauncher = launcher; } /** * By changing the WindowManagerProxy we can override the window insets information **/ private IWindowManager changeWindowManagerInstance(DeviceEmulationData deviceData) { WindowManagerProxy.INSTANCE.initializeForTesting( new TestWindowManagerProxy(mContext, deviceData)); return WindowManagerGlobal.getWindowManagerService(); } public <T> T emulate(DeviceEmulationData device, String grid, Callable<T> runInEmulation) throws Exception { WindowManagerProxy original = WindowManagerProxy.INSTANCE.get(mContext); // Set up emulation final int userId = UserHandle.myUserId(); WindowManagerProxy.INSTANCE.initializeForTesting( new TestWindowManagerProxy(mContext, device)); IWindowManager wm = changeWindowManagerInstance(device); // Change density twice to force display controller to reset its state wm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, device.density / 2, userId); wm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, device.density, userId); wm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, device.width, device.height); wm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1); // Set up grid setGrid(grid); try { return runInEmulation.call(); } finally { // Clear emulation WindowManagerProxy.INSTANCE.initializeForTesting(original); UiDevice.getInstance(getInstrumentation()).executeShellCommand("cmd window reset"); } } private void setGrid(String gridType) { // When the grid changes, the desktop arrangement get stored in SQL and we need to wait to // make sure there is no SQL operations running and get SQL_BUSY error, that's why we need // to call mLauncher.waitForLauncherInitialized(); mLauncher.waitForLauncherInitialized(); String testProviderAuthority = mContext.getPackageName() + ".grid_control"; Uri gridUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(testProviderAuthority) .appendPath("default_grid") .build(); ContentValues values = new ContentValues(); values.put("name", gridType); mContext.getContentResolver().update(gridUri, values, null, null); } }
tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.deviceemulator; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.view.Display; import android.view.WindowInsets; import com.android.launcher3.deviceemulator.models.DeviceEmulationData; import com.android.launcher3.util.RotationUtils; import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.window.CachedDisplayInfo; import com.android.launcher3.util.window.WindowManagerProxy; public class TestWindowManagerProxy extends WindowManagerProxy { private final DeviceEmulationData mDevice; public TestWindowManagerProxy(Context context, DeviceEmulationData device) { super(true); mDevice = device; } @Override public boolean isInternalDisplay(Display display) { return display.getDisplayId() == Display.DEFAULT_DISPLAY; } @Override protected int getDimenByName(String resName, Resources res) { Integer mock = mDevice.resourceOverrides.get(resName); return mock != null ? mock : super.getDimenByName(resName, res); } @Override public CachedDisplayInfo getDisplayInfo(Context context, Display display) { int rotation = display.getRotation(); Point size = new Point(mDevice.width, mDevice.height); RotationUtils.rotateSize(size, rotation); Rect cutout = new Rect(mDevice.cutout); RotationUtils.rotateRect(cutout, rotation); return new CachedDisplayInfo(getDisplayId(display), size, rotation, cutout); } @Override public WindowBounds getRealBounds(Context windowContext, Display display, CachedDisplayInfo info) { return estimateInternalDisplayBounds(windowContext) .get(getDisplayId(display)).second[display.getRotation()]; } @Override public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets, Rect outInsets) { outInsets.set(getRealBounds(context, context.getDisplay(), getDisplayInfo(context, context.getDisplay())).insets); return oldInsets; } }