diff options
3 files changed, 118 insertions, 3 deletions
diff --git a/packages/SystemUI/compose/gallery/AndroidManifest.xml b/packages/SystemUI/compose/gallery/AndroidManifest.xml index 562e1cc3339a..4fcce0bf6968 100644 --- a/packages/SystemUI/compose/gallery/AndroidManifest.xml +++ b/packages/SystemUI/compose/gallery/AndroidManifest.xml @@ -17,5 +17,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.systemui.compose.gallery"> - + <!-- To emulate a display size and density. --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> </manifest> diff --git a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml b/packages/SystemUI/compose/gallery/app/AndroidManifest.xml index 81132632079a..e7d496c2cb35 100644 --- a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml +++ b/packages/SystemUI/compose/gallery/app/AndroidManifest.xml @@ -16,7 +16,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.systemui.compose.gallery"> + package="com.android.systemui.compose.gallery.app"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" @@ -25,7 +25,7 @@ android:supportsRtl="true" android:theme="@style/Theme.SystemUIGallery"> <activity - android:name=".GalleryActivity" + android:name="com.android.systemui.compose.gallery.GalleryActivity" android:exported="true" android:label="@string/app_name"> <intent-filter> diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt index 65ea88a8eede..06bdad8a6735 100644 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt +++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt @@ -1,5 +1,10 @@ package com.android.systemui.compose.gallery +import android.graphics.Point +import android.os.UserHandle +import android.view.Display +import android.view.WindowManagerGlobal +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow @@ -9,14 +14,25 @@ import androidx.compose.material.icons.filled.BrightnessLow import androidx.compose.material.icons.filled.FormatSize import androidx.compose.material.icons.filled.FormatTextdirectionLToR import androidx.compose.material.icons.filled.FormatTextdirectionRToL +import androidx.compose.material.icons.filled.Smartphone +import androidx.compose.material.icons.filled.Tablet +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp +import kotlin.math.max +import kotlin.math.min enum class FontScale(val scale: Float) { Small(0.85f), @@ -35,6 +51,39 @@ fun ConfigurationControls( onChangeLayoutDirection: () -> Unit, onChangeFontScale: () -> Unit, ) { + // The display we are emulating, if any. + var emulatedDisplayName by rememberSaveable { mutableStateOf<String?>(null) } + val emulatedDisplay = + emulatedDisplayName?.let { name -> EmulatedDisplays.firstOrNull { it.name == name } } + + LaunchedEffect(emulatedDisplay) { + val wm = WindowManagerGlobal.getWindowManagerService() + + val defaultDisplayId = Display.DEFAULT_DISPLAY + if (emulatedDisplay == null) { + wm.clearForcedDisplayDensityForUser(defaultDisplayId, UserHandle.myUserId()) + wm.clearForcedDisplaySize(defaultDisplayId) + } else { + val density = emulatedDisplay.densityDpi + + // Emulate the display and make sure that we use the maximum available space possible. + val initialSize = Point() + wm.getInitialDisplaySize(defaultDisplayId, initialSize) + val width = emulatedDisplay.width + val height = emulatedDisplay.height + val minOfSize = min(width, height) + val maxOfSize = max(width, height) + if (initialSize.x < initialSize.y) { + wm.setForcedDisplaySize(defaultDisplayId, minOfSize, maxOfSize) + } else { + wm.setForcedDisplaySize(defaultDisplayId, maxOfSize, minOfSize) + } + wm.setForcedDisplayDensityForUser(defaultDisplayId, density, UserHandle.myUserId()) + } + } + + // TODO(b/231131244): Fork FlowRow from Accompanist and use that instead to make sure that users + // don't miss any available configuration. LazyRow { // Dark/light theme. item { @@ -82,5 +131,70 @@ fun ConfigurationControls( } } } + + // Display emulation. + EmulatedDisplays.forEach { display -> + item { + DisplayButton( + display, + emulatedDisplay == display, + { emulatedDisplayName = it?.name }, + ) + } + } + } +} + +@Composable +private fun DisplayButton( + display: EmulatedDisplay, + selected: Boolean, + onChangeEmulatedDisplay: (EmulatedDisplay?) -> Unit, +) { + val onClick = { + if (selected) { + onChangeEmulatedDisplay(null) + } else { + onChangeEmulatedDisplay(display) + } + } + + val content: @Composable RowScope.() -> Unit = { + Icon(display.icon, null) + Spacer(Modifier.width(8.dp)) + Text(display.name) + } + + if (selected) { + Button(onClick, contentPadding = ButtonDefaults.TextButtonContentPadding, content = content) + } else { + TextButton(onClick, content = content) } } + +/** The displays that can be emulated from this Gallery app. */ +private val EmulatedDisplays = + listOf( + EmulatedDisplay( + "Phone", + Icons.Default.Smartphone, + width = 1440, + height = 3120, + densityDpi = 560, + ), + EmulatedDisplay( + "Tablet", + Icons.Default.Tablet, + width = 2560, + height = 1600, + densityDpi = 320, + ), + ) + +private data class EmulatedDisplay( + val name: String, + val icon: ImageVector, + val width: Int, + val height: Int, + val densityDpi: Int, +) |