diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 0c19e4bab1..485b36c174 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -41,7 +41,6 @@ import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; -import android.util.TypedValue; import android.util.Xml; import android.view.Display; @@ -58,11 +57,9 @@ import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.Info; -import com.android.launcher3.util.IntArray; import com.android.launcher3.util.LockedUserState; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Partner; -import com.android.launcher3.util.Themes; import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.window.WindowManagerProxy; @@ -76,6 +73,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; public class InvariantDeviceProfile { @@ -151,8 +149,6 @@ public class InvariantDeviceProfile { public float[] transientTaskbarIconSize; - private SparseArray mExtraAttrs; - /** * Number of icons inside the hotseat area. */ @@ -360,8 +356,6 @@ public class InvariantDeviceProfile { inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing; - mExtraAttrs = closestProfile.extraAttrs; - iconSize = displayOption.iconSizes; float maxIconSize = iconSize[0]; for (int i = 1; i < iconSize.length; i++) { @@ -495,9 +489,8 @@ public class InvariantDeviceProfile { if ((type == XmlPullParser.START_TAG) && GridOption.TAG_NAME.equals(parser.getName())) { - GridOption gridOption = new GridOption(context, Xml.asAttributeSet(parser), - deviceType); - if (gridOption.isEnabled || allowDisabledGrid) { + GridOption gridOption = new GridOption(context, Xml.asAttributeSet(parser)); + if (gridOption.isEnabled(deviceType) || allowDisabledGrid) { final int displayDepth = parser.getDepth(); while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > displayDepth) @@ -519,7 +512,7 @@ public class InvariantDeviceProfile { if (!TextUtils.isEmpty(gridName)) { for (DisplayOption option : profiles) { if (gridName.equals(option.grid.name) - && (option.grid.isEnabled || allowDisabledGrid)) { + && (option.grid.isEnabled(deviceType) || allowDisabledGrid)) { filteredProfiles.add(option); } } @@ -542,6 +535,16 @@ public class InvariantDeviceProfile { * @return all the grid options that can be shown on the device */ public List parseAllGridOptions(Context context) { + return parseAllDefinedGridOptions(context) + .stream() + .filter(go -> go.isEnabled(deviceType)) + .collect(Collectors.toList()); + } + + /** + * @return all the grid options that can be shown on the device + */ + public static List parseAllDefinedGridOptions(Context context) { List result = new ArrayList<>(); try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) { @@ -551,11 +554,7 @@ public class InvariantDeviceProfile { || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { if ((type == XmlPullParser.START_TAG) && GridOption.TAG_NAME.equals(parser.getName())) { - GridOption option = - new GridOption(context, Xml.asAttributeSet(parser), deviceType); - if (option.isEnabled) { - result.add(option); - } + result.add(new GridOption(context, Xml.asAttributeSet(parser))); } } } catch (IOException | XmlPullParserException e) { @@ -774,7 +773,7 @@ public class InvariantDeviceProfile { public final int numRows; public final int numColumns; public final int numSearchContainerColumns; - public final boolean isEnabled; + public final int deviceCategory; private final int numFolderRows; private final int numFolderColumns; @@ -800,9 +799,7 @@ public class InvariantDeviceProfile { private final boolean isScalable; private final int devicePaddingId; - private final SparseArray extraAttrs; - - public GridOption(Context context, AttributeSet attrs, @DeviceType int deviceType) { + public GridOption(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.GridDisplayOption); name = a.getString(R.styleable.GridDisplayOption_name); @@ -859,16 +856,8 @@ public class InvariantDeviceProfile { R.styleable.GridDisplayOption_isScalable, false); devicePaddingId = a.getResourceId( R.styleable.GridDisplayOption_devicePaddingId, INVALID_RESOURCE_HANDLE); - - int deviceCategory = a.getInt(R.styleable.GridDisplayOption_deviceCategory, + deviceCategory = a.getInt(R.styleable.GridDisplayOption_deviceCategory, DEVICE_CATEGORY_ALL); - isEnabled = (deviceType == TYPE_PHONE - && ((deviceCategory & DEVICE_CATEGORY_PHONE) == DEVICE_CATEGORY_PHONE)) - || (deviceType == TYPE_TABLET - && ((deviceCategory & DEVICE_CATEGORY_TABLET) == DEVICE_CATEGORY_TABLET)) - || (deviceType == TYPE_MULTI_DISPLAY - && ((deviceCategory & DEVICE_CATEGORY_MULTI_DISPLAY) - == DEVICE_CATEGORY_MULTI_DISPLAY)); int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb, DONT_INLINE_QSB); @@ -884,8 +873,20 @@ public class InvariantDeviceProfile { == INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE; a.recycle(); - extraAttrs = Themes.createValueMap(context, attrs, - IntArray.wrap(R.styleable.GridDisplayOption)); + } + + public boolean isEnabled(@DeviceType int deviceType) { + switch (deviceType) { + case TYPE_PHONE: + return (deviceCategory & DEVICE_CATEGORY_PHONE) == DEVICE_CATEGORY_PHONE; + case TYPE_TABLET: + return (deviceCategory & DEVICE_CATEGORY_TABLET) == DEVICE_CATEGORY_TABLET; + case TYPE_MULTI_DISPLAY: + return (deviceCategory & DEVICE_CATEGORY_MULTI_DISPLAY) + == DEVICE_CATEGORY_MULTI_DISPLAY; + default: + return false; + } } } diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java index a3261b2623..02ebb15cd1 100644 --- a/src/com/android/launcher3/util/DisplayController.java +++ b/src/com/android/launcher3/util/DisplayController.java @@ -392,6 +392,13 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()), densityDpi); } + /** + * Returns all displays for the device + */ + public Set getAllDisplays() { + return Collections.unmodifiableSet(mPerDisplayBounds.keySet()); + } + public int getDensityDpi() { return densityDpi; } diff --git a/tests/res/raw/devices.json b/tests/res/raw/devices.json deleted file mode 100644 index a78dd86464..0000000000 --- a/tests/res/raw/devices.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "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 - } - } -} diff --git a/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java b/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java deleted file mode 100644 index e2ed65fa5b..0000000000 --- a/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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(deviceData)); - return WindowManagerGlobal.getWindowManagerService(); - } - - public T emulate(DeviceEmulationData device, String grid, Callable runInEmulation) - throws Exception { - WindowManagerProxy original = WindowManagerProxy.INSTANCE.get(mContext); - // Set up emulation - final int userId = UserHandle.myUserId(); - WindowManagerProxy.INSTANCE.initializeForTesting(new TestWindowManagerProxy(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); - } -} diff --git a/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java b/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java deleted file mode 100644 index 2d6bbccb68..0000000000 --- a/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.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(DeviceEmulationData device) { - super(true); - mDevice = device; - } - - @Override - protected int getDimenByName(Resources res, String resName) { - Integer mock = mDevice.resourceOverrides.get(resName); - return mock != null ? mock : super.getDimenByName(res, resName); - } - - @Override - protected int getDimenByName(Resources res, String resName, String fallback) { - return getDimenByName(res, resName); - } - - @Override - public CachedDisplayInfo getDisplayInfo(Context displayInfoContext) { - int rotation = getRotation(displayInfoContext); - 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(size, rotation, cutout); - } - - @Override - public WindowBounds getRealBounds(Context displayInfoContext, CachedDisplayInfo info) { - return estimateInternalDisplayBounds(displayInfoContext).get( - getDisplayInfo(displayInfoContext))[getDisplay(displayInfoContext).getRotation()]; - } - - @Override - public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets, - Rect outInsets) { - outInsets.set(getRealBounds(context, getDisplayInfo(context)).insets); - return oldInsets; - } -} diff --git a/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java b/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java deleted file mode 100644 index 55b7bf199b..0000000000 --- a/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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.models; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT; -import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE; -import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE; -import static com.android.launcher3.testing.shared.ResourceUtils.getDimenByName; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Rect; -import android.os.Build; -import android.util.ArrayMap; - -import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.util.DisplayController; -import com.android.launcher3.util.IOUtils; -import com.android.launcher3.util.IntArray; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.InputStream; -import java.util.Arrays; -import java.util.Map; - -public class DeviceEmulationData { - - public final int width; - public final int height; - public final int density; - public final String name; - public final String[] grids; - public final Rect cutout; - public final Map resourceOverrides; - - private static final String[] EMULATED_SYSTEM_RESOURCES = new String[]{ - NAVBAR_HEIGHT, - NAVBAR_HEIGHT_LANDSCAPE, - NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, - "status_bar_height", - }; - - public DeviceEmulationData(int width, int height, int density, Rect cutout, String name, - String[] grid, - Map resourceOverrides) { - this.width = width; - this.height = height; - this.density = density; - this.name = name; - this.grids = grid; - this.cutout = cutout; - this.resourceOverrides = resourceOverrides; - } - - public static DeviceEmulationData deviceFromJSON(JSONObject json) throws JSONException { - int width = json.getInt("width"); - int height = json.getInt("height"); - int density = json.getInt("density"); - String name = json.getString("name"); - - JSONArray gridArray = json.getJSONArray("grids"); - String[] grids = new String[gridArray.length()]; - for (int i = 0, count = grids.length; i < count; i++) { - grids[i] = gridArray.getString(i); - } - - IntArray deviceCutout = IntArray.fromConcatString(json.getString("cutout")); - Rect cutout = new Rect(deviceCutout.get(0), deviceCutout.get(1), deviceCutout.get(2), - deviceCutout.get(3)); - - - JSONObject resourceOverridesJson = json.getJSONObject("resourceOverrides"); - Map resourceOverrides = new ArrayMap<>(); - for (String key : resourceOverridesJson.keySet()) { - resourceOverrides.put(key, resourceOverridesJson.getInt(key)); - } - return new DeviceEmulationData(width, height, density, cutout, name, grids, - resourceOverrides); - } - - @Override - public String toString() { - JSONObject json = new JSONObject(); - try { - json.put("width", width); - json.put("height", height); - json.put("density", density); - json.put("name", name); - json.put("cutout", IntArray.wrap( - cutout.left, cutout.top, cutout.right, cutout.bottom).toConcatString()); - - JSONArray gridArray = new JSONArray(); - Arrays.stream(grids).forEach(gridArray::put); - json.put("grids", gridArray); - - - JSONObject resourceOverrides = new JSONObject(); - for (Map.Entry e : this.resourceOverrides.entrySet()) { - resourceOverrides.put(e.getKey(), e.getValue()); - } - json.put("resourceOverrides", resourceOverrides); - } catch (Exception e) { - e.printStackTrace(); - } - return json.toString(); - } - - public static DeviceEmulationData getCurrentDeviceData(Context context) { - DisplayController.Info info = DisplayController.INSTANCE.get(context).getInfo(); - String[] grids = InvariantDeviceProfile.INSTANCE.get(context) - .parseAllGridOptions(context).stream() - .map(go -> go.name).toArray(String[]::new); - String code = Build.MODEL.replaceAll("\\s", "").toLowerCase(); - - Map resourceOverrides = new ArrayMap<>(); - for (String s : EMULATED_SYSTEM_RESOURCES) { - resourceOverrides.put(s, getDimenByName(s, context.getResources(), 0)); - } - return new DeviceEmulationData(info.currentSize.x, info.currentSize.y, - info.getDensityDpi(), info.cutout, code, grids, resourceOverrides); - } - - public static DeviceEmulationData getDevice(String deviceCode) throws Exception { - return DeviceEmulationData.deviceFromJSON(readJSON().getJSONObject(deviceCode)); - } - - private static JSONObject readJSON() throws Exception { - Context context = getInstrumentation().getContext(); - Resources myRes = context.getResources(); - int resId = myRes.getIdentifier("devices", "raw", context.getPackageName()); - try (InputStream is = myRes.openRawResource(resId)) { - return new JSONObject(new String(IOUtils.toByteArray(is))); - } - } - -}