Loading packages/SystemUI/compose/gallery/AndroidManifest.xml +2 −1 Original line number Diff line number Diff line Loading @@ -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> packages/SystemUI/compose/gallery/app/AndroidManifest.xml +2 −2 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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> Loading packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt +114 −0 Original line number Diff line number Diff line 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 Loading @@ -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), Loading @@ -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 { Loading Loading @@ -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, ) Loading
packages/SystemUI/compose/gallery/AndroidManifest.xml +2 −1 Original line number Diff line number Diff line Loading @@ -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>
packages/SystemUI/compose/gallery/app/AndroidManifest.xml +2 −2 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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> Loading
packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt +114 −0 Original line number Diff line number Diff line 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 Loading @@ -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), Loading @@ -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 { Loading Loading @@ -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, )