Merge "Making the Launcher Customizar available to Launcher3 and not only Nexus" into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
73dd3d3723
@@ -0,0 +1,11 @@
|
||||
filegroup {
|
||||
name: "launcher-emulator-util",
|
||||
srcs: [
|
||||
"models/DeviceEmulationData.kt",
|
||||
"models/EmulationParams.kt",
|
||||
"models/EmulatorDisplay.kt",
|
||||
"models/LauncherDeviceList.kt",
|
||||
"DensityPicker.java",
|
||||
"TestWindowManagerProxy.java",
|
||||
],
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.util.launcheremulator;
|
||||
|
||||
import com.android.launcher3.util.launcheremulator.models.DeviceEmulationData;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Utility class to get the different densities used when going to setting and changing the density
|
||||
* of the display
|
||||
*/
|
||||
public class DensityPicker {
|
||||
|
||||
private static final Density[] SUMMARIES_SMALLER = new Density[] { Density.SMALL };
|
||||
private static final Density[] SUMMARIES_LARGER = new Density[] {
|
||||
Density.LARGE, Density.LARGER, Density.LARGEST};
|
||||
|
||||
/**
|
||||
* Defines the available densities to pick from
|
||||
*/
|
||||
public enum Density {
|
||||
SMALL, NORMAL, LARGE, LARGER, LARGEST;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return returns a map defining the different density for a given device, the map entries are
|
||||
* defined in {@code Densities}
|
||||
*/
|
||||
public static Map<Density, Integer> getDisplayEntries(DeviceEmulationData device) {
|
||||
// Display logic copied from
|
||||
// packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
|
||||
// Compute number of "larger" and "smaller" scales for this display.
|
||||
final int minDimensionPx = Math.min(device.width, device.height);
|
||||
final int maxDensity = minDimensionPx / 2;
|
||||
final float maxScale = Math.min(device.densityMaxScale,
|
||||
maxDensity / (float) device.density);
|
||||
final float minScale = device.densityMinScale;
|
||||
final float minScaleInterval = device.densityMinScaleInterval;
|
||||
final int defaultDensity = device.density;
|
||||
|
||||
final int numLarger = (int) Math.max(0, Math.min((maxScale - 1) / minScaleInterval,
|
||||
SUMMARIES_LARGER.length));
|
||||
final int numSmaller = (int) Math.max(0, Math.min((1 - minScale) / minScaleInterval,
|
||||
SUMMARIES_SMALLER.length));
|
||||
|
||||
Map<Density, Integer> displayEntries = new HashMap<>();
|
||||
displayEntries.put(Density.NORMAL, defaultDensity);
|
||||
if (numSmaller > 0) {
|
||||
final float interval = (1 - minScale) / numSmaller;
|
||||
for (int i = numSmaller - 1; i >= 0; i--) {
|
||||
// Round down to a multiple of 2 by truncating the low bit.
|
||||
final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
|
||||
displayEntries.put(SUMMARIES_SMALLER[i], density);
|
||||
}
|
||||
}
|
||||
|
||||
if (numLarger > 0) {
|
||||
final float interval = (maxScale - 1) / numLarger;
|
||||
for (int i = 0; i < numLarger; i++) {
|
||||
// Round down to a multiple of 2 by truncating the low bit.
|
||||
final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
|
||||
displayEntries.put(SUMMARIES_LARGER[i], density);
|
||||
}
|
||||
}
|
||||
return displayEntries;
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.util.launcheremulator;
|
||||
|
||||
import static android.view.Surface.ROTATION_0;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import com.android.launcher3.util.DisplayController.Info;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
import com.android.launcher3.util.launcheremulator.models.DeviceEmulationData;
|
||||
import com.android.launcher3.util.window.CachedDisplayInfo;
|
||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Testing class that overrides some values of the Launcher in order to be able to emulate it.
|
||||
* This class overrides the singleton of {@code WindowManagerProxy}.
|
||||
*/
|
||||
public class TestWindowManagerProxy extends WindowManagerProxy {
|
||||
|
||||
private DeviceEmulationData mDevice;
|
||||
|
||||
private boolean mIsInDesktopMode;
|
||||
|
||||
/**
|
||||
* Constructor to be used when initiating using xml overrides.
|
||||
*
|
||||
* Use a new DisplayController.Info object to avoid circular dependency when initiating
|
||||
* DisplayController
|
||||
*/
|
||||
public TestWindowManagerProxy(Context context) {
|
||||
this(DeviceEmulationData.Companion.getCurrentDeviceData(context, new Info(context)));
|
||||
}
|
||||
|
||||
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) {
|
||||
return mDevice.toCachedDisplayInfo(getRotation(displayInfoContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowBounds getRealBounds(Context displayInfoContext, CachedDisplayInfo info) {
|
||||
List<WindowBounds> windowBounds = estimateInternalDisplayBounds(displayInfoContext).get(
|
||||
getDisplayInfo(displayInfoContext).normalize(this));
|
||||
return windowBounds.get(getDisplay(displayInfoContext).getRotation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets,
|
||||
Rect outInsets) {
|
||||
|
||||
boolean isGesture = isGestureNav(context);
|
||||
outInsets.set(getRealBounds(context, getDisplayInfo(context)).insets);
|
||||
|
||||
WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(oldInsets);
|
||||
|
||||
// This is the same implementation used in WindowManagerProxy to prevent the taskbar
|
||||
// size to be count in the inset. It overrides the tappable bottom inset to be 0
|
||||
// for gesture nav (otherwise taskbar would count towards it).
|
||||
// This is used for the bottom protection in All Apps for example.
|
||||
if (isGesture) {
|
||||
Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
|
||||
Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
|
||||
oldTappableInsets.right, 0);
|
||||
insetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
|
||||
}
|
||||
|
||||
return insetsBuilder.build();
|
||||
}
|
||||
|
||||
protected CachedDisplayInfo getSecondaryDisplayInfo(int rotation) {
|
||||
return mDevice.secondDisplay.toCachedDisplayInfo(rotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of normalized info of internal displays to estimated window bounds
|
||||
* for that display
|
||||
*/
|
||||
@Override
|
||||
public ArrayMap<CachedDisplayInfo, List<WindowBounds>> estimateInternalDisplayBounds(
|
||||
Context displayInfoContext) {
|
||||
ArrayMap<CachedDisplayInfo, List<WindowBounds>> result =
|
||||
super.estimateInternalDisplayBounds(displayInfoContext);
|
||||
if (mDevice.secondDisplay == null) {
|
||||
return result;
|
||||
}
|
||||
CachedDisplayInfo info = getSecondaryDisplayInfo(ROTATION_0).normalize(this);
|
||||
result.put(info, estimateWindowBounds(displayInfoContext, info));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInDesktopMode(int displayId) {
|
||||
return mIsInDesktopMode;
|
||||
}
|
||||
|
||||
public void setInDesktopMode(boolean isInDesktopMode) {
|
||||
mIsInDesktopMode = isInDesktopMode;
|
||||
}
|
||||
|
||||
public void setDevice(DeviceEmulationData device) {
|
||||
mDevice = device;
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.util.launcheremulator.models
|
||||
|
||||
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.InvariantDeviceProfile.DeviceType
|
||||
import com.android.launcher3.testing.shared.ResourceUtils
|
||||
import com.android.launcher3.util.DisplayController
|
||||
import com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH
|
||||
import java.util.Locale
|
||||
import kotlin.math.min
|
||||
|
||||
/** Model class that holds the data needed to emulate a device display. */
|
||||
class DeviceEmulationData(
|
||||
val name: String,
|
||||
width: Int,
|
||||
height: Int,
|
||||
density: Int,
|
||||
@JvmField val densityMaxScale: Float,
|
||||
@JvmField val densityMinScale: Float,
|
||||
@JvmField val densityMinScaleInterval: Float,
|
||||
cutout: Rect,
|
||||
var defaultGrid: String,
|
||||
@JvmField val resourceOverrides: Map<String, Int>,
|
||||
@JvmField val secondDisplay: EmulatorDisplay?,
|
||||
val supportsFixedLandscape: Boolean,
|
||||
) : EmulatorDisplay(width, height, density, cutout) {
|
||||
|
||||
override fun toString(): String {
|
||||
val secondDisplayString =
|
||||
secondDisplay?.let { "EmulatorDisplay(${it.propString("\n\t\t")})" } ?: "null"
|
||||
val resourcesString =
|
||||
resourceOverrides.entries.joinToString { "\n\t\t\"${it.key}\" to ${it.value}" }
|
||||
return "DeviceEmulationData(" +
|
||||
"\n\tname = \"$name\"," +
|
||||
propString("\n\t") +
|
||||
"," +
|
||||
"\n\tdensityMaxScale = ${densityMaxScale}f," +
|
||||
"\n\tdensityMinScale = ${densityMinScale}f," +
|
||||
"\n\tdensityMinScaleInterval = ${densityMinScaleInterval}f," +
|
||||
"\n\tdefaultGrid = \"${defaultGrid}\"," +
|
||||
"\n\tsecondDisplay = $secondDisplayString," +
|
||||
"\n\tresourceOverrides = mapOf($resourcesString)" +
|
||||
"\n)"
|
||||
}
|
||||
|
||||
/** Returns if the device is in tablet dimension */
|
||||
fun isTablet() = (min(width, height).toFloat() / (density / 160)) > MIN_TABLET_WIDTH
|
||||
|
||||
companion object {
|
||||
private const val DENSITY_MIN_SCALE = 0.85f
|
||||
private const val DENSITY_MAX_SCALE = 1.5f
|
||||
private const val DENSITY_SCALE_INTERVAL = 0.09f
|
||||
private val EMULATED_SYSTEM_RESOURCES =
|
||||
arrayOf(
|
||||
ResourceUtils.NAVBAR_HEIGHT,
|
||||
ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE,
|
||||
ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE,
|
||||
ResourceUtils.STATUS_BAR_HEIGHT,
|
||||
ResourceUtils.STATUS_BAR_HEIGHT_LANDSCAPE,
|
||||
ResourceUtils.STATUS_BAR_HEIGHT_PORTRAIT,
|
||||
)
|
||||
|
||||
const val FIXED_LANDSCAPE_GRID = "fixed_landscape_mode"
|
||||
|
||||
private fun getFraction(c: Context, resName: String, fallback: Float): Float {
|
||||
val resId = c.resources.getIdentifier(resName, "fraction", c.packageName)
|
||||
return if (resId != Resources.ID_NULL) c.resources.getFraction(resId, 1, 1)
|
||||
else fallback
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a `DeviceEmulationData` representing the current device.
|
||||
*
|
||||
* @param context Launcher context.
|
||||
* @return `DeviceEmulationData`
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun getCurrentDeviceData(
|
||||
context: Context,
|
||||
info: DisplayController.Info = DisplayController.INSTANCE[context].info,
|
||||
): DeviceEmulationData {
|
||||
val code = Build.MODEL.replace("\\s".toRegex(), "").lowercase(Locale.getDefault())
|
||||
val resourceOverrides: MutableMap<String, Int> = ArrayMap()
|
||||
for (s in EMULATED_SYSTEM_RESOURCES) {
|
||||
resourceOverrides[s] = ResourceUtils.getDimenByName(s, context.resources, 0)
|
||||
}
|
||||
|
||||
// Print overridden resources
|
||||
val settingsCtx =
|
||||
try {
|
||||
context.createPackageContext("com.android.settings", 0)
|
||||
} catch (e: Exception) {
|
||||
context
|
||||
}
|
||||
val displayDensityMaxScale =
|
||||
getFraction(settingsCtx, "display_density_max_scale", DENSITY_MAX_SCALE)
|
||||
val displayDensityMinScale =
|
||||
getFraction(settingsCtx, "display_density_min_scale", DENSITY_MIN_SCALE)
|
||||
val displayDensityMinScaleInterval =
|
||||
getFraction(
|
||||
settingsCtx,
|
||||
"display_density_min_scale_interval",
|
||||
DENSITY_SCALE_INTERVAL,
|
||||
)
|
||||
|
||||
@DeviceType val deviceType = info.deviceType
|
||||
val defaultGrid =
|
||||
InvariantDeviceProfile.parseAllDefinedGridOptions(context, info)
|
||||
.stream()
|
||||
.filter { it.isEnabled(deviceType) }
|
||||
.map { it.name }
|
||||
.findFirst()
|
||||
.get()
|
||||
|
||||
return DeviceEmulationData(
|
||||
name = code,
|
||||
width = info.currentSize.x,
|
||||
height = info.currentSize.y,
|
||||
density = info.densityDpi,
|
||||
densityMaxScale = displayDensityMaxScale,
|
||||
densityMinScale = displayDensityMinScale,
|
||||
densityMinScaleInterval = displayDensityMinScaleInterval,
|
||||
cutout = info.cutout,
|
||||
defaultGrid = defaultGrid,
|
||||
resourceOverrides = resourceOverrides,
|
||||
secondDisplay = null,
|
||||
supportsFixedLandscape = deviceType == InvariantDeviceProfile.TYPE_PHONE,
|
||||
)
|
||||
}
|
||||
|
||||
/** Returns a stored `DeviceEmulationData` */
|
||||
@JvmStatic
|
||||
fun getDevice(deviceCode: String) =
|
||||
LauncherDeviceList.ALL_DEVICES.find { it.name == deviceCode }!!
|
||||
}
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.util.launcheremulator.models
|
||||
|
||||
import com.android.launcher3.util.launcheremulator.DensityPicker.Density
|
||||
import com.android.launcher3.util.launcheremulator.models.DeviceEmulationData.Companion.FIXED_LANDSCAPE_GRID
|
||||
import com.android.launcher3.util.launcheremulator.models.EmulationParamsUtils.getDensityShort
|
||||
import com.android.launcher3.util.launcheremulator.models.EmulationParamsUtils.getDeviceShort
|
||||
import com.android.launcher3.util.launcheremulator.models.EmulationParamsUtils.getFontScaleShort
|
||||
import com.android.launcher3.util.launcheremulator.models.EmulationParamsUtils.getGridShort
|
||||
import com.android.launcher3.util.launcheremulator.models.EmulationParamsUtils.getOrientationShort
|
||||
import com.android.launcher3.util.launcheremulator.models.EmulationParamsUtils.getRtlShort
|
||||
import com.android.launcher3.util.launcheremulator.models.EmulationParamsUtils.isDarkThemeShort
|
||||
import com.android.launcher3.util.launcheremulator.models.EmulationParamsUtils.isUsingThemeIconsShort
|
||||
import com.android.launcher3.util.launcheremulator.models.LauncherOrientation.LANDSCAPE
|
||||
import com.android.launcher3.util.launcheremulator.models.LauncherOrientation.PORTRAIT
|
||||
import com.android.launcher3.util.launcheremulator.models.LauncherOrientation.SEASCAPE
|
||||
import java.util.Locale
|
||||
|
||||
/** Class to hold all parameters used for device and launcher config emulation */
|
||||
data class EmulationParams
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
val device: DeviceEmulationData,
|
||||
val density: Density = Density.NORMAL,
|
||||
val grid: String = device.defaultGrid,
|
||||
val orientation: LauncherOrientation = PORTRAIT,
|
||||
val fontScale: FontScale = FontScale.DEFAULT,
|
||||
val isRtl: Boolean = false,
|
||||
val isDarkTheme: Boolean = false,
|
||||
val isUsingThemeIcons: Boolean = false,
|
||||
) {
|
||||
|
||||
init {
|
||||
validateParameters()
|
||||
}
|
||||
|
||||
val deviceType: DeviceType =
|
||||
when {
|
||||
device.secondDisplay != null -> DeviceType.Foldable
|
||||
device.isTablet() -> DeviceType.Tablet
|
||||
else -> DeviceType.Phone
|
||||
}
|
||||
|
||||
val isFixedLandscape = grid == FIXED_LANDSCAPE_GRID && orientation == LANDSCAPE
|
||||
|
||||
override fun toString(): String =
|
||||
listOf(
|
||||
getDeviceShort(device),
|
||||
getGridShort(grid),
|
||||
getDensityShort(density),
|
||||
getOrientationShort(orientation),
|
||||
getFontScaleShort(fontScale),
|
||||
getRtlShort(isRtl),
|
||||
isDarkThemeShort(isDarkTheme),
|
||||
isUsingThemeIconsShort(isUsingThemeIcons),
|
||||
)
|
||||
.filter { it.isNotBlank() }
|
||||
.joinToString("_")
|
||||
|
||||
enum class DeviceType {
|
||||
Phone,
|
||||
Tablet,
|
||||
Foldable,
|
||||
}
|
||||
|
||||
private fun validateParameters() {
|
||||
if (isFixedLandscape && !device.supportsFixedLandscape) {
|
||||
throw Exception(
|
||||
"Device ${device.name} doesn't support fixed landscape, update " +
|
||||
"EmulationParams or update the DeviceList.kt"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object EmulationParamsUtils {
|
||||
|
||||
fun getDeviceShort(device: DeviceEmulationData): String =
|
||||
when (device.name) {
|
||||
"pixel5" -> "p5"
|
||||
"pixel6" -> "p6"
|
||||
"pixel6pro" -> "p6pro"
|
||||
"pixel7pro" -> "p7pro"
|
||||
"pixelFoldable2023" -> "fold"
|
||||
"pixelFoldable2023_frontDisplay" -> "foldFront"
|
||||
"pixelTablet2023" -> "tab"
|
||||
else -> device.name
|
||||
}
|
||||
|
||||
fun getGridShort(grid: String) =
|
||||
when (grid) {
|
||||
"normal" -> "5x5"
|
||||
"practical" -> "4x5"
|
||||
"reasonable" -> "4x4"
|
||||
"big" -> "3x3"
|
||||
"crazy_big" -> "2x2"
|
||||
"tablet_normal" -> "6x5"
|
||||
else -> grid
|
||||
}
|
||||
|
||||
fun getDensityShort(density: Density) =
|
||||
"den" +
|
||||
when (density) {
|
||||
Density.SMALL -> "S"
|
||||
Density.NORMAL -> "N"
|
||||
Density.LARGE -> "L"
|
||||
Density.LARGER -> "L2"
|
||||
Density.LARGEST -> "L3"
|
||||
}
|
||||
|
||||
fun getOrientationShort(orientation: LauncherOrientation) =
|
||||
when (orientation) {
|
||||
PORTRAIT -> "port"
|
||||
LANDSCAPE -> "land"
|
||||
SEASCAPE -> "seas"
|
||||
}
|
||||
|
||||
fun getFontScaleShort(fontScale: FontScale) =
|
||||
if (fontScale != FontScale.DEFAULT) "font${fontScale.value}" else ""
|
||||
|
||||
fun getRtlShort(isRtl: Boolean) = if (isRtl) "rtl" else ""
|
||||
|
||||
fun isDarkThemeShort(isDarkTheme: Boolean) = if (isDarkTheme) "dark" else ""
|
||||
|
||||
fun isUsingThemeIconsShort(isUsingThemeIcons: Boolean) =
|
||||
if (isUsingThemeIcons) "thIcons" else ""
|
||||
}
|
||||
|
||||
enum class LauncherOrientation {
|
||||
PORTRAIT,
|
||||
LANDSCAPE,
|
||||
SEASCAPE,
|
||||
}
|
||||
|
||||
enum class FontScale(val value: Float) {
|
||||
SMALL(0.85f),
|
||||
DEFAULT(1f),
|
||||
LARGEST(2f);
|
||||
|
||||
override fun toString() = name.lowercase(Locale.getDefault())
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.util.launcheremulator.models
|
||||
|
||||
import android.graphics.Insets
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.view.DisplayCutout
|
||||
import com.android.launcher3.util.RotationUtils
|
||||
import com.android.launcher3.util.window.CachedDisplayInfo
|
||||
|
||||
/** Represents the model for the display in DeviceList */
|
||||
open class EmulatorDisplay(
|
||||
@JvmField val width: Int,
|
||||
@JvmField val height: Int,
|
||||
@JvmField val density: Int,
|
||||
val cutout: Rect,
|
||||
) {
|
||||
|
||||
fun propString(prefix: String) =
|
||||
"${prefix}width = $width,${prefix}height = $height,${prefix}density = $density,${prefix}cutout = Rect(${cutout.left}, ${cutout.top}, ${cutout.right}, ${cutout.bottom})"
|
||||
|
||||
/**
|
||||
* Returns a CachedDisplayInfo rotated to the given rotation, representing the current emulation
|
||||
*/
|
||||
fun toCachedDisplayInfo(rotation: Int): CachedDisplayInfo {
|
||||
val size = Point(width, height)
|
||||
RotationUtils.rotateSize(size, rotation)
|
||||
val cutoutRotated = Rect(cutout)
|
||||
RotationUtils.rotateRect(cutoutRotated, rotation)
|
||||
return CachedDisplayInfo(
|
||||
size,
|
||||
rotation,
|
||||
DisplayCutout(Insets.of(cutoutRotated), null, null, null, null),
|
||||
)
|
||||
}
|
||||
}
|
||||
+391
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.util.launcheremulator.models
|
||||
|
||||
import android.graphics.Rect
|
||||
|
||||
object LauncherDeviceList {
|
||||
|
||||
val pixel9pro =
|
||||
DeviceEmulationData(
|
||||
name = "pixel9pro",
|
||||
width = 960,
|
||||
height = 2142,
|
||||
density = 360,
|
||||
cutout = Rect(0, 153, 0, 0),
|
||||
densityMaxScale = 1.5f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09000003f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay = null,
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 153,
|
||||
"status_bar_height" to 153,
|
||||
"navigation_bar_height_landscape" to 54,
|
||||
"navigation_bar_height" to 54,
|
||||
"status_bar_height_landscape" to 54,
|
||||
"navigation_bar_width" to 54,
|
||||
),
|
||||
supportsFixedLandscape = false,
|
||||
)
|
||||
|
||||
val pixel9proFold =
|
||||
DeviceEmulationData(
|
||||
name = "pixel9profold",
|
||||
width = 2076,
|
||||
height = 2152,
|
||||
density = 390,
|
||||
cutout = Rect(0, 136, 0, 0),
|
||||
densityMaxScale = 1.3849792f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09000003f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay =
|
||||
EmulatorDisplay(
|
||||
width = 1080,
|
||||
height = 2424,
|
||||
density = 390,
|
||||
cutout = Rect(0, 152, 0, 0),
|
||||
),
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 88,
|
||||
"status_bar_height" to 88,
|
||||
"navigation_bar_height_landscape" to 59,
|
||||
"navigation_bar_height" to 59,
|
||||
"status_bar_height_landscape" to 88,
|
||||
"navigation_bar_width" to 59,
|
||||
),
|
||||
supportsFixedLandscape = false,
|
||||
)
|
||||
|
||||
val pixel9proFold_frontDisplay =
|
||||
DeviceEmulationData(
|
||||
name = "pixel9profold_front",
|
||||
width = 1080,
|
||||
height = 2424,
|
||||
density = 390,
|
||||
cutout = Rect(0, 152, 0, 0),
|
||||
densityMaxScale = 1.3849792f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09000003f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay =
|
||||
EmulatorDisplay(
|
||||
width = 2076,
|
||||
height = 2152,
|
||||
density = 390,
|
||||
cutout = Rect(0, 136, 0, 0),
|
||||
),
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 161,
|
||||
"status_bar_height" to 161,
|
||||
"navigation_bar_height_landscape" to 59,
|
||||
"navigation_bar_height" to 59,
|
||||
"status_bar_height_landscape" to 59,
|
||||
"navigation_bar_width" to 59,
|
||||
),
|
||||
supportsFixedLandscape = false,
|
||||
)
|
||||
|
||||
val pixel8proHD =
|
||||
DeviceEmulationData(
|
||||
name = "p8pro_qhd",
|
||||
width = 1344,
|
||||
height = 2992,
|
||||
density = 480,
|
||||
cutout = Rect(0, 151, 0, 0),
|
||||
densityMaxScale = 1.5f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay = null,
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 151,
|
||||
"status_bar_height" to 151,
|
||||
"navigation_bar_height_landscape" to 72,
|
||||
"navigation_bar_height" to 72,
|
||||
"status_bar_height_landscape" to 84,
|
||||
"navigation_bar_width" to 72,
|
||||
),
|
||||
supportsFixedLandscape = true,
|
||||
)
|
||||
|
||||
val pixel8proFHD =
|
||||
DeviceEmulationData(
|
||||
name = "p8pro_fhd",
|
||||
width = 1008,
|
||||
height = 2244,
|
||||
density = 360,
|
||||
cutout = Rect(0, 113, 0, 0),
|
||||
densityMaxScale = 1.5f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay = null,
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 151,
|
||||
"status_bar_height" to 151,
|
||||
"navigation_bar_height_landscape" to 54,
|
||||
"navigation_bar_height" to 54,
|
||||
"status_bar_height_landscape" to 63,
|
||||
"navigation_bar_width" to 54,
|
||||
),
|
||||
supportsFixedLandscape = true,
|
||||
)
|
||||
|
||||
val pixel8 =
|
||||
DeviceEmulationData(
|
||||
name = "p8",
|
||||
width = 1080,
|
||||
height = 2400,
|
||||
density = 420,
|
||||
cutout = Rect(0, 132, 0, 0),
|
||||
densityMaxScale = 1.5f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay = null,
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 132,
|
||||
"status_bar_height" to 132,
|
||||
"navigation_bar_height_landscape" to 63,
|
||||
"navigation_bar_height" to 63,
|
||||
"status_bar_height_landscape" to 74,
|
||||
"navigation_bar_width" to 63,
|
||||
),
|
||||
supportsFixedLandscape = true,
|
||||
)
|
||||
|
||||
val pixel7pro =
|
||||
DeviceEmulationData(
|
||||
name = "pixel7pro",
|
||||
width = 1080,
|
||||
height = 2340,
|
||||
density = 420,
|
||||
cutout = Rect(0, 98, 0, 0),
|
||||
densityMaxScale = 1.5f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay = null,
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 130,
|
||||
"status_bar_height" to 130,
|
||||
"navigation_bar_height_landscape" to 63,
|
||||
"navigation_bar_height" to 63,
|
||||
"status_bar_height_landscape" to 74,
|
||||
"navigation_bar_width" to 63,
|
||||
),
|
||||
supportsFixedLandscape = true,
|
||||
)
|
||||
|
||||
val pixel6pro =
|
||||
DeviceEmulationData(
|
||||
name = "pixel6pro",
|
||||
width = 1440,
|
||||
height = 3120,
|
||||
density = 560,
|
||||
cutout = Rect(0, 130, 0, 0),
|
||||
densityMaxScale = 1.5f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay = null,
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 130,
|
||||
"status_bar_height" to 130,
|
||||
"navigation_bar_height_landscape" to 84,
|
||||
"navigation_bar_height" to 84,
|
||||
"status_bar_height_landscape" to 98,
|
||||
"navigation_bar_width" to 84,
|
||||
),
|
||||
supportsFixedLandscape = true,
|
||||
)
|
||||
|
||||
val pixel6 =
|
||||
DeviceEmulationData(
|
||||
name = "pixel6",
|
||||
width = 1080,
|
||||
height = 2400,
|
||||
density = 420,
|
||||
cutout = Rect(0, 118, 0, 0),
|
||||
densityMaxScale = 1.5f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay = null,
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 118,
|
||||
"status_bar_height" to 118,
|
||||
"navigation_bar_height_landscape" to 63,
|
||||
"navigation_bar_height" to 63,
|
||||
"status_bar_height_landscape" to 74,
|
||||
"navigation_bar_width" to 63,
|
||||
),
|
||||
supportsFixedLandscape = true,
|
||||
)
|
||||
|
||||
val pixelTablet2023 =
|
||||
DeviceEmulationData(
|
||||
name = "pixelTablet2023",
|
||||
width = 2560,
|
||||
height = 1600,
|
||||
density = 320,
|
||||
cutout = Rect(0, 0, 0, 0),
|
||||
densityMaxScale = 1.3312378f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09f,
|
||||
defaultGrid = "tablet_normal",
|
||||
secondDisplay = null,
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 104,
|
||||
"status_bar_height" to 104,
|
||||
"navigation_bar_height_landscape" to 48,
|
||||
"navigation_bar_height" to 48,
|
||||
"status_bar_height_landscape" to 104,
|
||||
"navigation_bar_width" to 48,
|
||||
),
|
||||
supportsFixedLandscape = false,
|
||||
)
|
||||
|
||||
val pixelFold2023 =
|
||||
DeviceEmulationData(
|
||||
name = "pixelFoldable2023",
|
||||
width = 2208,
|
||||
height = 1840,
|
||||
density = 420,
|
||||
cutout = Rect(0, 0, 0, 0),
|
||||
densityMaxScale = 1.1679993f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay =
|
||||
EmulatorDisplay(
|
||||
width = 1080,
|
||||
height = 2092,
|
||||
density = 420,
|
||||
cutout = Rect(0, 133, 0, 0),
|
||||
),
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 110,
|
||||
"status_bar_height" to 110,
|
||||
"navigation_bar_height_landscape" to 63,
|
||||
"navigation_bar_height" to 63,
|
||||
"status_bar_height_landscape" to 110,
|
||||
"navigation_bar_width" to 63,
|
||||
),
|
||||
supportsFixedLandscape = false,
|
||||
)
|
||||
|
||||
val pixelFold2023_frontDisplay =
|
||||
DeviceEmulationData(
|
||||
name = "pixelFoldable2023_frontDisplay",
|
||||
width = 1080,
|
||||
height = 2092,
|
||||
density = 420,
|
||||
cutout = Rect(0, 133, 0, 0),
|
||||
densityMaxScale = 1.1679993f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay =
|
||||
EmulatorDisplay(
|
||||
width = 2208,
|
||||
height = 1840,
|
||||
density = 420,
|
||||
cutout = Rect(0, 0, 0, 0),
|
||||
),
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 133,
|
||||
"status_bar_height" to 133,
|
||||
"navigation_bar_height_landscape" to 63,
|
||||
"navigation_bar_height" to 63,
|
||||
"status_bar_height_landscape" to 110,
|
||||
"navigation_bar_width" to 63,
|
||||
),
|
||||
supportsFixedLandscape = false,
|
||||
)
|
||||
|
||||
val pixel5 =
|
||||
DeviceEmulationData(
|
||||
name = "pixel5",
|
||||
width = 1080,
|
||||
height = 2340,
|
||||
density = 440,
|
||||
cutout = Rect(0, 136, 0, 0),
|
||||
densityMaxScale = 1.5f,
|
||||
densityMinScale = 0.85f,
|
||||
densityMinScaleInterval = 0.09f,
|
||||
defaultGrid = "medium",
|
||||
secondDisplay = null,
|
||||
resourceOverrides =
|
||||
mapOf(
|
||||
"status_bar_height_portrait" to 136,
|
||||
"status_bar_height" to 136,
|
||||
"navigation_bar_height_landscape" to 66,
|
||||
"navigation_bar_height" to 66,
|
||||
"status_bar_height_landscape" to 77,
|
||||
"navigation_bar_width" to 66,
|
||||
),
|
||||
supportsFixedLandscape = true,
|
||||
)
|
||||
|
||||
// This is the list of devices that are currently under development, we keep the other list in
|
||||
// case special tests are needed for other devices
|
||||
val CURRENT_DEVICES =
|
||||
listOf(
|
||||
pixel9pro,
|
||||
pixel9proFold,
|
||||
pixel9proFold_frontDisplay,
|
||||
pixel8proHD,
|
||||
pixel8proFHD,
|
||||
pixel8,
|
||||
pixel7pro,
|
||||
pixelTablet2023,
|
||||
pixelFold2023,
|
||||
pixelFold2023_frontDisplay,
|
||||
)
|
||||
|
||||
val ALL_DEVICES =
|
||||
listOf(
|
||||
pixel9pro,
|
||||
pixel9proFold,
|
||||
pixel9proFold_frontDisplay,
|
||||
pixel8proHD,
|
||||
pixel8proFHD,
|
||||
pixel8,
|
||||
pixel7pro,
|
||||
pixel6pro,
|
||||
pixelTablet2023,
|
||||
pixelFold2023,
|
||||
pixelFold2023_frontDisplay,
|
||||
pixel6,
|
||||
pixel5,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user