Merge "Making the Launcher Customizar available to Launcher3 and not only Nexus" into main

This commit is contained in:
Sebastián Franco
2025-05-06 08:56:51 -07:00
committed by Android (Google) Code Review
11 changed files with 1419 additions and 0 deletions
@@ -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",
],
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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 }!!
}
}
@@ -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())
}
@@ -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),
)
}
}
@@ -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,
)
}