Merge "Move the compose code to tm-qpr-dev (1/2)" into tm-qpr-dev
38
packages/SystemUI/compose/core/Android.bp
Normal file
@@ -0,0 +1,38 @@
|
||||
// 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 {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
|
||||
}
|
||||
|
||||
android_library {
|
||||
name: "SystemUIComposeCore",
|
||||
manifest: "AndroidManifest.xml",
|
||||
|
||||
srcs: [
|
||||
"src/**/*.kt",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"androidx.compose.runtime_runtime",
|
||||
"androidx.compose.material3_material3",
|
||||
],
|
||||
|
||||
kotlincflags: ["-Xjvm-default=all"],
|
||||
}
|
||||
22
packages/SystemUI/compose/core/AndroidManifest.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.systemui.compose.core">
|
||||
|
||||
|
||||
</manifest>
|
||||
37
packages/SystemUI/compose/core/TEST_MAPPING
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "SystemUIComposeCoreTests",
|
||||
"options": [
|
||||
{
|
||||
"exclude-annotation": "org.junit.Ignore"
|
||||
},
|
||||
{
|
||||
"exclude-annotation": "androidx.test.filters.FlakyTest"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SystemUIComposeFeaturesTests",
|
||||
"options": [
|
||||
{
|
||||
"exclude-annotation": "org.junit.Ignore"
|
||||
},
|
||||
{
|
||||
"exclude-annotation": "androidx.test.filters.FlakyTest"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SystemUIComposeGalleryTests",
|
||||
"options": [
|
||||
{
|
||||
"exclude-annotation": "org.junit.Ignore"
|
||||
},
|
||||
{
|
||||
"exclude-annotation": "androidx.test.filters.FlakyTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* 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.systemui.compose
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.window.DialogWindowProvider
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
|
||||
/**
|
||||
* *************************************************************************************************
|
||||
* This file was forked from
|
||||
* https://github.com/google/accompanist/blob/main/systemuicontroller/src/main/java/com/google/accompanist/systemuicontroller/SystemUiController.kt
|
||||
* and will be removed once it lands in AndroidX.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class which provides easy-to-use utilities for updating the System UI bar colors within Jetpack
|
||||
* Compose.
|
||||
*
|
||||
* @sample com.google.accompanist.sample.systemuicontroller.SystemUiControllerSample
|
||||
*/
|
||||
@Stable
|
||||
interface SystemUiController {
|
||||
|
||||
/**
|
||||
* Property which holds the status bar visibility. If set to true, show the status bar,
|
||||
* otherwise hide the status bar.
|
||||
*/
|
||||
var isStatusBarVisible: Boolean
|
||||
|
||||
/**
|
||||
* Property which holds the navigation bar visibility. If set to true, show the navigation bar,
|
||||
* otherwise hide the navigation bar.
|
||||
*/
|
||||
var isNavigationBarVisible: Boolean
|
||||
|
||||
/**
|
||||
* Property which holds the status & navigation bar visibility. If set to true, show both bars,
|
||||
* otherwise hide both bars.
|
||||
*/
|
||||
var isSystemBarsVisible: Boolean
|
||||
get() = isNavigationBarVisible && isStatusBarVisible
|
||||
set(value) {
|
||||
isStatusBarVisible = value
|
||||
isNavigationBarVisible = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status bar color.
|
||||
*
|
||||
* @param color The **desired** [Color] to set. This may require modification if running on an
|
||||
* API level that only supports white status bar icons.
|
||||
* @param darkIcons Whether dark status bar icons would be preferable.
|
||||
* @param transformColorForLightContent A lambda which will be invoked to transform [color] if
|
||||
* dark icons were requested but are not available. Defaults to applying a black scrim.
|
||||
*
|
||||
* @see statusBarDarkContentEnabled
|
||||
*/
|
||||
fun setStatusBarColor(
|
||||
color: Color,
|
||||
darkIcons: Boolean = color.luminance() > 0.5f,
|
||||
transformColorForLightContent: (Color) -> Color = BlackScrimmed
|
||||
)
|
||||
|
||||
/**
|
||||
* Set the navigation bar color.
|
||||
*
|
||||
* @param color The **desired** [Color] to set. This may require modification if running on an
|
||||
* API level that only supports white navigation bar icons. Additionally this will be ignored
|
||||
* and [Color.Transparent] will be used on API 29+ where gesture navigation is preferred or the
|
||||
* system UI automatically applies background protection in other navigation modes.
|
||||
* @param darkIcons Whether dark navigation bar icons would be preferable.
|
||||
* @param navigationBarContrastEnforced Whether the system should ensure that the navigation bar
|
||||
* has enough contrast when a fully transparent background is requested. Only supported on API
|
||||
* 29+.
|
||||
* @param transformColorForLightContent A lambda which will be invoked to transform [color] if
|
||||
* dark icons were requested but are not available. Defaults to applying a black scrim.
|
||||
*
|
||||
* @see navigationBarDarkContentEnabled
|
||||
* @see navigationBarContrastEnforced
|
||||
*/
|
||||
fun setNavigationBarColor(
|
||||
color: Color,
|
||||
darkIcons: Boolean = color.luminance() > 0.5f,
|
||||
navigationBarContrastEnforced: Boolean = true,
|
||||
transformColorForLightContent: (Color) -> Color = BlackScrimmed
|
||||
)
|
||||
|
||||
/**
|
||||
* Set the status and navigation bars to [color].
|
||||
*
|
||||
* @see setStatusBarColor
|
||||
* @see setNavigationBarColor
|
||||
*/
|
||||
fun setSystemBarsColor(
|
||||
color: Color,
|
||||
darkIcons: Boolean = color.luminance() > 0.5f,
|
||||
isNavigationBarContrastEnforced: Boolean = true,
|
||||
transformColorForLightContent: (Color) -> Color = BlackScrimmed
|
||||
) {
|
||||
setStatusBarColor(color, darkIcons, transformColorForLightContent)
|
||||
setNavigationBarColor(
|
||||
color,
|
||||
darkIcons,
|
||||
isNavigationBarContrastEnforced,
|
||||
transformColorForLightContent
|
||||
)
|
||||
}
|
||||
|
||||
/** Property which holds whether the status bar icons + content are 'dark' or not. */
|
||||
var statusBarDarkContentEnabled: Boolean
|
||||
|
||||
/** Property which holds whether the navigation bar icons + content are 'dark' or not. */
|
||||
var navigationBarDarkContentEnabled: Boolean
|
||||
|
||||
/**
|
||||
* Property which holds whether the status & navigation bar icons + content are 'dark' or not.
|
||||
*/
|
||||
var systemBarsDarkContentEnabled: Boolean
|
||||
get() = statusBarDarkContentEnabled && navigationBarDarkContentEnabled
|
||||
set(value) {
|
||||
statusBarDarkContentEnabled = value
|
||||
navigationBarDarkContentEnabled = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Property which holds whether the system is ensuring that the navigation bar has enough
|
||||
* contrast when a fully transparent background is requested. Only has an affect when running on
|
||||
* Android API 29+ devices.
|
||||
*/
|
||||
var isNavigationBarContrastEnforced: Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Remembers a [SystemUiController] for the given [window].
|
||||
*
|
||||
* If no [window] is provided, an attempt to find the correct [Window] is made.
|
||||
*
|
||||
* First, if the [LocalView]'s parent is a [DialogWindowProvider], then that dialog's [Window] will
|
||||
* be used.
|
||||
*
|
||||
* Second, we attempt to find [Window] for the [Activity] containing the [LocalView].
|
||||
*
|
||||
* If none of these are found (such as may happen in a preview), then the functionality of the
|
||||
* returned [SystemUiController] will be degraded, but won't throw an exception.
|
||||
*/
|
||||
@Composable
|
||||
fun rememberSystemUiController(
|
||||
window: Window? = findWindow(),
|
||||
): SystemUiController {
|
||||
val view = LocalView.current
|
||||
return remember(view, window) { AndroidSystemUiController(view, window) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun findWindow(): Window? =
|
||||
(LocalView.current.parent as? DialogWindowProvider)?.window
|
||||
?: LocalView.current.context.findWindow()
|
||||
|
||||
private tailrec fun Context.findWindow(): Window? =
|
||||
when (this) {
|
||||
is Activity -> window
|
||||
is ContextWrapper -> baseContext.findWindow()
|
||||
else -> null
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class for setting the navigation and status bar colors for a [View], gracefully
|
||||
* degrading behavior based upon API level.
|
||||
*
|
||||
* Typically you would use [rememberSystemUiController] to remember an instance of this.
|
||||
*/
|
||||
internal class AndroidSystemUiController(private val view: View, private val window: Window?) :
|
||||
SystemUiController {
|
||||
private val windowInsetsController = window?.let { WindowCompat.getInsetsController(it, view) }
|
||||
|
||||
override fun setStatusBarColor(
|
||||
color: Color,
|
||||
darkIcons: Boolean,
|
||||
transformColorForLightContent: (Color) -> Color
|
||||
) {
|
||||
statusBarDarkContentEnabled = darkIcons
|
||||
|
||||
window?.statusBarColor =
|
||||
when {
|
||||
darkIcons && windowInsetsController?.isAppearanceLightStatusBars != true -> {
|
||||
// If we're set to use dark icons, but our windowInsetsController call didn't
|
||||
// succeed (usually due to API level), we instead transform the color to
|
||||
// maintain contrast
|
||||
transformColorForLightContent(color)
|
||||
}
|
||||
else -> color
|
||||
}.toArgb()
|
||||
}
|
||||
|
||||
override fun setNavigationBarColor(
|
||||
color: Color,
|
||||
darkIcons: Boolean,
|
||||
navigationBarContrastEnforced: Boolean,
|
||||
transformColorForLightContent: (Color) -> Color
|
||||
) {
|
||||
navigationBarDarkContentEnabled = darkIcons
|
||||
isNavigationBarContrastEnforced = navigationBarContrastEnforced
|
||||
|
||||
window?.navigationBarColor =
|
||||
when {
|
||||
darkIcons && windowInsetsController?.isAppearanceLightNavigationBars != true -> {
|
||||
// If we're set to use dark icons, but our windowInsetsController call didn't
|
||||
// succeed (usually due to API level), we instead transform the color to
|
||||
// maintain contrast
|
||||
transformColorForLightContent(color)
|
||||
}
|
||||
else -> color
|
||||
}.toArgb()
|
||||
}
|
||||
|
||||
override var isStatusBarVisible: Boolean
|
||||
get() {
|
||||
return ViewCompat.getRootWindowInsets(view)
|
||||
?.isVisible(WindowInsetsCompat.Type.statusBars()) == true
|
||||
}
|
||||
set(value) {
|
||||
if (value) {
|
||||
windowInsetsController?.show(WindowInsetsCompat.Type.statusBars())
|
||||
} else {
|
||||
windowInsetsController?.hide(WindowInsetsCompat.Type.statusBars())
|
||||
}
|
||||
}
|
||||
|
||||
override var isNavigationBarVisible: Boolean
|
||||
get() {
|
||||
return ViewCompat.getRootWindowInsets(view)
|
||||
?.isVisible(WindowInsetsCompat.Type.navigationBars()) == true
|
||||
}
|
||||
set(value) {
|
||||
if (value) {
|
||||
windowInsetsController?.show(WindowInsetsCompat.Type.navigationBars())
|
||||
} else {
|
||||
windowInsetsController?.hide(WindowInsetsCompat.Type.navigationBars())
|
||||
}
|
||||
}
|
||||
|
||||
override var statusBarDarkContentEnabled: Boolean
|
||||
get() = windowInsetsController?.isAppearanceLightStatusBars == true
|
||||
set(value) {
|
||||
windowInsetsController?.isAppearanceLightStatusBars = value
|
||||
}
|
||||
|
||||
override var navigationBarDarkContentEnabled: Boolean
|
||||
get() = windowInsetsController?.isAppearanceLightNavigationBars == true
|
||||
set(value) {
|
||||
windowInsetsController?.isAppearanceLightNavigationBars = value
|
||||
}
|
||||
|
||||
override var isNavigationBarContrastEnforced: Boolean
|
||||
get() = Build.VERSION.SDK_INT >= 29 && window?.isNavigationBarContrastEnforced == true
|
||||
set(value) {
|
||||
if (Build.VERSION.SDK_INT >= 29) {
|
||||
window?.isNavigationBarContrastEnforced = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val BlackScrim = Color(0f, 0f, 0f, 0.3f) // 30% opaque black
|
||||
private val BlackScrimmed: (Color) -> Color = { original -> BlackScrim.compositeOver(original) }
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.systemui.compose.theme
|
||||
|
||||
import android.annotation.ColorInt
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.android.internal.R
|
||||
|
||||
/** CompositionLocal used to pass [AndroidColorScheme] down the tree. */
|
||||
val LocalAndroidColorScheme =
|
||||
staticCompositionLocalOf<AndroidColorScheme> {
|
||||
throw IllegalStateException(
|
||||
"No AndroidColorScheme configured. Make sure to use LocalAndroidColorScheme in a " +
|
||||
"Composable surrounded by a SystemUITheme {}."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The Android color scheme.
|
||||
*
|
||||
* Important: Use M3 colors from MaterialTheme.colorScheme whenever possible instead. In the future,
|
||||
* most of the colors in this class will be removed in favor of their M3 counterpart.
|
||||
*/
|
||||
class AndroidColorScheme internal constructor(context: Context) {
|
||||
val colorPrimary = getColor(context, R.attr.colorPrimary)
|
||||
val colorPrimaryDark = getColor(context, R.attr.colorPrimaryDark)
|
||||
val colorAccent = getColor(context, R.attr.colorAccent)
|
||||
val colorAccentPrimary = getColor(context, R.attr.colorAccentPrimary)
|
||||
val colorAccentSecondary = getColor(context, R.attr.colorAccentSecondary)
|
||||
val colorAccentTertiary = getColor(context, R.attr.colorAccentTertiary)
|
||||
val colorAccentPrimaryVariant = getColor(context, R.attr.colorAccentPrimaryVariant)
|
||||
val colorAccentSecondaryVariant = getColor(context, R.attr.colorAccentSecondaryVariant)
|
||||
val colorAccentTertiaryVariant = getColor(context, R.attr.colorAccentTertiaryVariant)
|
||||
val colorSurface = getColor(context, R.attr.colorSurface)
|
||||
val colorSurfaceHighlight = getColor(context, R.attr.colorSurfaceHighlight)
|
||||
val colorSurfaceVariant = getColor(context, R.attr.colorSurfaceVariant)
|
||||
val colorSurfaceHeader = getColor(context, R.attr.colorSurfaceHeader)
|
||||
val colorError = getColor(context, R.attr.colorError)
|
||||
val colorBackground = getColor(context, R.attr.colorBackground)
|
||||
val colorBackgroundFloating = getColor(context, R.attr.colorBackgroundFloating)
|
||||
val panelColorBackground = getColor(context, R.attr.panelColorBackground)
|
||||
val textColorPrimary = getColor(context, R.attr.textColorPrimary)
|
||||
val textColorSecondary = getColor(context, R.attr.textColorSecondary)
|
||||
val textColorTertiary = getColor(context, R.attr.textColorTertiary)
|
||||
val textColorPrimaryInverse = getColor(context, R.attr.textColorPrimaryInverse)
|
||||
val textColorSecondaryInverse = getColor(context, R.attr.textColorSecondaryInverse)
|
||||
val textColorTertiaryInverse = getColor(context, R.attr.textColorTertiaryInverse)
|
||||
val textColorOnAccent = getColor(context, R.attr.textColorOnAccent)
|
||||
val colorForeground = getColor(context, R.attr.colorForeground)
|
||||
val colorForegroundInverse = getColor(context, R.attr.colorForegroundInverse)
|
||||
|
||||
private fun getColor(context: Context, attr: Int): Color {
|
||||
val ta = context.obtainStyledAttributes(intArrayOf(attr))
|
||||
@ColorInt val color = ta.getColor(0, 0)
|
||||
ta.recycle()
|
||||
return Color(color)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.systemui.compose.theme
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
/** The Material 3 theme that should wrap all SystemUI Composables. */
|
||||
@Composable
|
||||
fun SystemUITheme(
|
||||
isDarkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
// TODO(b/230605885): Define our typography and color scheme.
|
||||
val colorScheme =
|
||||
if (isDarkTheme) {
|
||||
dynamicDarkColorScheme(context)
|
||||
} else {
|
||||
dynamicLightColorScheme(context)
|
||||
}
|
||||
val androidColorScheme = AndroidColorScheme(context)
|
||||
val typography = Typography()
|
||||
|
||||
MaterialTheme(colorScheme, typography = typography) {
|
||||
CompositionLocalProvider(
|
||||
LocalAndroidColorScheme provides androidColorScheme,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
48
packages/SystemUI/compose/core/tests/Android.bp
Normal file
@@ -0,0 +1,48 @@
|
||||
// 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 {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
|
||||
}
|
||||
|
||||
// TODO(b/230606318): Make those host tests instead of device tests.
|
||||
android_test {
|
||||
name: "SystemUIComposeCoreTests",
|
||||
manifest: "AndroidManifest.xml",
|
||||
test_suites: ["device-tests"],
|
||||
sdk_version: "current",
|
||||
certificate: "platform",
|
||||
|
||||
srcs: [
|
||||
"src/**/*.kt",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"SystemUIComposeCore",
|
||||
|
||||
"androidx.test.runner",
|
||||
"androidx.test.ext.junit",
|
||||
|
||||
"androidx.compose.runtime_runtime",
|
||||
"androidx.compose.ui_ui-test-junit4",
|
||||
"androidx.compose.ui_ui-test-manifest",
|
||||
],
|
||||
|
||||
kotlincflags: ["-Xjvm-default=enable"],
|
||||
}
|
||||
28
packages/SystemUI/compose/core/tests/AndroidManifest.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.systemui.compose.core.tests" >
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.systemui.compose.core.tests"
|
||||
android:label="Tests for SystemUIComposeCore"/>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.systemui.compose.theme
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Assert.assertThrows
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SystemUIThemeTest {
|
||||
@get:Rule val composeRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun testThemeShowsContent() {
|
||||
composeRule.setContent { SystemUITheme { Text("foo") } }
|
||||
|
||||
composeRule.onNodeWithText("foo").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAndroidColorsAreAvailableInsideTheme() {
|
||||
composeRule.setContent {
|
||||
SystemUITheme { Text("foo", color = LocalAndroidColorScheme.current.colorAccent) }
|
||||
}
|
||||
|
||||
composeRule.onNodeWithText("foo").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAccessingAndroidColorsWithoutThemeThrows() {
|
||||
assertThrows(IllegalStateException::class.java) {
|
||||
composeRule.setContent {
|
||||
Text("foo", color = LocalAndroidColorScheme.current.colorAccent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
packages/SystemUI/compose/features/Android.bp
Normal file
@@ -0,0 +1,40 @@
|
||||
// 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 {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
|
||||
}
|
||||
|
||||
android_library {
|
||||
name: "SystemUIComposeFeatures",
|
||||
manifest: "AndroidManifest.xml",
|
||||
|
||||
srcs: [
|
||||
"src/**/*.kt",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"SystemUIComposeCore",
|
||||
|
||||
"androidx.compose.runtime_runtime",
|
||||
"androidx.compose.material3_material3",
|
||||
],
|
||||
|
||||
kotlincflags: ["-Xjvm-default=all"],
|
||||
}
|
||||
22
packages/SystemUI/compose/features/AndroidManifest.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.systemui.compose.features">
|
||||
|
||||
|
||||
</manifest>
|
||||
26
packages/SystemUI/compose/features/TEST_MAPPING
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "SystemUIComposeFeaturesTests",
|
||||
"options": [
|
||||
{
|
||||
"exclude-annotation": "org.junit.Ignore"
|
||||
},
|
||||
{
|
||||
"exclude-annotation": "androidx.test.filters.FlakyTest"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SystemUIComposeGalleryTests",
|
||||
"options": [
|
||||
{
|
||||
"exclude-annotation": "org.junit.Ignore"
|
||||
},
|
||||
{
|
||||
"exclude-annotation": "androidx.test.filters.FlakyTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.systemui
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* This is an example Compose feature, which shows a text and a count that is incremented when
|
||||
* clicked. We also show the max width available to this component, which is displayed either next
|
||||
* to or below the text depending on that max width.
|
||||
*/
|
||||
@Composable
|
||||
fun ExampleFeature(text: String, modifier: Modifier = Modifier) {
|
||||
BoxWithConstraints(modifier) {
|
||||
val maxWidth = maxWidth
|
||||
if (maxWidth < 600.dp) {
|
||||
Column {
|
||||
CounterTile(text)
|
||||
Spacer(Modifier.size(16.dp))
|
||||
MaxWidthTile(maxWidth)
|
||||
}
|
||||
} else {
|
||||
Row {
|
||||
CounterTile(text)
|
||||
Spacer(Modifier.size(16.dp))
|
||||
MaxWidthTile(maxWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CounterTile(text: String, modifier: Modifier = Modifier) {
|
||||
Surface(
|
||||
modifier,
|
||||
color = MaterialTheme.colorScheme.primaryContainer,
|
||||
shape = RoundedCornerShape(28.dp),
|
||||
) {
|
||||
var count by remember { mutableStateOf(0) }
|
||||
Column(
|
||||
Modifier.clickable { count++ }.padding(16.dp),
|
||||
) {
|
||||
Text(text)
|
||||
Text("I was clicked $count times.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MaxWidthTile(maxWidth: Dp, modifier: Modifier = Modifier) {
|
||||
Surface(
|
||||
modifier,
|
||||
color = MaterialTheme.colorScheme.tertiaryContainer,
|
||||
shape = RoundedCornerShape(28.dp),
|
||||
) {
|
||||
Text(
|
||||
"The max available width to me is: ${maxWidth.value.roundToInt()}dp",
|
||||
Modifier.padding(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
48
packages/SystemUI/compose/features/tests/Android.bp
Normal file
@@ -0,0 +1,48 @@
|
||||
// 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 {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
|
||||
}
|
||||
|
||||
// TODO(b/230606318): Make those host tests instead of device tests.
|
||||
android_test {
|
||||
name: "SystemUIComposeFeaturesTests",
|
||||
manifest: "AndroidManifest.xml",
|
||||
test_suites: ["device-tests"],
|
||||
sdk_version: "current",
|
||||
certificate: "platform",
|
||||
|
||||
srcs: [
|
||||
"src/**/*.kt",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"SystemUIComposeFeatures",
|
||||
|
||||
"androidx.test.runner",
|
||||
"androidx.test.ext.junit",
|
||||
|
||||
"androidx.compose.runtime_runtime",
|
||||
"androidx.compose.ui_ui-test-junit4",
|
||||
"androidx.compose.ui_ui-test-manifest",
|
||||
],
|
||||
|
||||
kotlincflags: ["-Xjvm-default=enable"],
|
||||
}
|
||||
28
packages/SystemUI/compose/features/tests/AndroidManifest.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.systemui.compose.features.tests" >
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.systemui.compose.features.tests"
|
||||
android:label="Tests for SystemUIComposeFeatures"/>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.systemui
|
||||
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleFeatureTest {
|
||||
@get:Rule val composeRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun testProvidedTextIsDisplayed() {
|
||||
composeRule.setContent { ExampleFeature("foo") }
|
||||
|
||||
composeRule.onNodeWithText("foo").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCountIsIncreasedWhenClicking() {
|
||||
composeRule.setContent { ExampleFeature("foo") }
|
||||
|
||||
composeRule.onNodeWithText("I was clicked 0 times.").assertIsDisplayed().performClick()
|
||||
composeRule.onNodeWithText("I was clicked 1 times.").assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
72
packages/SystemUI/compose/gallery/Android.bp
Normal file
@@ -0,0 +1,72 @@
|
||||
// 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 {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
|
||||
}
|
||||
|
||||
android_library {
|
||||
name: "SystemUIComposeGalleryLib",
|
||||
manifest: "AndroidManifest.xml",
|
||||
|
||||
srcs: [
|
||||
"src/**/*.kt",
|
||||
],
|
||||
|
||||
resource_dirs: [
|
||||
"res",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"SystemUI-core",
|
||||
"SystemUIComposeCore",
|
||||
"SystemUIComposeFeatures",
|
||||
|
||||
"androidx.compose.runtime_runtime",
|
||||
"androidx.compose.material3_material3",
|
||||
"androidx.compose.material_material-icons-extended",
|
||||
"androidx.activity_activity-compose",
|
||||
"androidx.navigation_navigation-compose",
|
||||
|
||||
"androidx.appcompat_appcompat",
|
||||
],
|
||||
|
||||
kotlincflags: ["-Xjvm-default=all"],
|
||||
}
|
||||
|
||||
android_app {
|
||||
name: "SystemUIComposeGallery",
|
||||
defaults: ["platform_app_defaults"],
|
||||
manifest: "app/AndroidManifest.xml",
|
||||
|
||||
static_libs: [
|
||||
"SystemUIComposeGalleryLib",
|
||||
],
|
||||
|
||||
platform_apis: true,
|
||||
system_ext_specific: true,
|
||||
certificate: "platform",
|
||||
privileged: true,
|
||||
|
||||
optimize: {
|
||||
proguard_flags_files: ["proguard-rules.pro"],
|
||||
},
|
||||
|
||||
dxflags: ["--multi-dex"],
|
||||
}
|
||||
55
packages/SystemUI/compose/gallery/AndroidManifest.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.android.systemui.compose.gallery">
|
||||
<!-- To emulate a display size and density. -->
|
||||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
|
||||
|
||||
<application
|
||||
android:name="android.app.Application"
|
||||
android:appComponentFactory="androidx.core.app.AppComponentFactory"
|
||||
tools:replace="android:name,android:appComponentFactory">
|
||||
<!-- Disable providers from SystemUI -->
|
||||
<provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider"
|
||||
android:authorities="com.android.systemui.test.keyguard.disabled"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove" />
|
||||
<provider android:name="com.google.android.systemui.keyguard.KeyguardSliceProviderGoogle"
|
||||
android:authorities="com.android.systemui.test.keyguard.disabled"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove" />
|
||||
<provider android:name="com.android.keyguard.clock.ClockOptionsProvider"
|
||||
android:authorities="com.android.systemui.test.keyguard.clock.disabled"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove" />
|
||||
<provider android:name="com.android.systemui.people.PeopleProvider"
|
||||
android:authorities="com.android.systemui.test.people.disabled"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove" />
|
||||
<provider android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.android.systemui.test.fileprovider.disabled"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
tools:node="remove"/>
|
||||
</application>
|
||||
</manifest>
|
||||
15
packages/SystemUI/compose/gallery/TEST_MAPPING
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "SystemUIComposeGalleryTests",
|
||||
"options": [
|
||||
{
|
||||
"exclude-annotation": "org.junit.Ignore"
|
||||
},
|
||||
{
|
||||
"exclude-annotation": "androidx.test.filters.FlakyTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
39
packages/SystemUI/compose/gallery/app/AndroidManifest.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.android.systemui.compose.gallery.app">
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.SystemUI.Gallery"
|
||||
tools:replace="android:icon,android:theme,android:label">
|
||||
<activity
|
||||
android:name="com.android.systemui.compose.gallery.GalleryActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
21
packages/SystemUI/compose/gallery/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
</vector>
|
||||
@@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
19
packages/SystemUI/compose/gallery/res/values/colors.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
20
packages/SystemUI/compose/gallery/res/values/strings.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Application name [CHAR LIMIT=NONE] -->
|
||||
<string name="app_name">SystemUI Gallery</string>
|
||||
</resources>
|
||||
30
packages/SystemUI/compose/gallery/res/values/themes.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="Theme.SystemUI.Gallery">
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
|
||||
<item name="android:statusBarColor" tools:targetApi="l">
|
||||
@android:color/transparent
|
||||
</item>
|
||||
<item name="android:navigationBarColor" tools:targetApi="l">
|
||||
@android:color/transparent
|
||||
</item>
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.systemui.compose.gallery
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.android.systemui.compose.theme.LocalAndroidColorScheme
|
||||
|
||||
/** The screen that shows all the Material 3 colors. */
|
||||
@Composable
|
||||
fun MaterialColorsScreen() {
|
||||
val colors = MaterialTheme.colorScheme
|
||||
ColorsScreen(
|
||||
listOf(
|
||||
"primary" to colors.primary,
|
||||
"onPrimary" to colors.onPrimary,
|
||||
"primaryContainer" to colors.primaryContainer,
|
||||
"onPrimaryContainer" to colors.onPrimaryContainer,
|
||||
"inversePrimary" to colors.inversePrimary,
|
||||
"secondary" to colors.secondary,
|
||||
"onSecondary" to colors.onSecondary,
|
||||
"secondaryContainer" to colors.secondaryContainer,
|
||||
"onSecondaryContainer" to colors.onSecondaryContainer,
|
||||
"tertiary" to colors.tertiary,
|
||||
"onTertiary" to colors.onTertiary,
|
||||
"tertiaryContainer" to colors.tertiaryContainer,
|
||||
"onTertiaryContainer" to colors.onTertiaryContainer,
|
||||
"background" to colors.background,
|
||||
"onBackground" to colors.onBackground,
|
||||
"surface" to colors.surface,
|
||||
"onSurface" to colors.onSurface,
|
||||
"surfaceVariant" to colors.surfaceVariant,
|
||||
"onSurfaceVariant" to colors.onSurfaceVariant,
|
||||
"inverseSurface" to colors.inverseSurface,
|
||||
"inverseOnSurface" to colors.inverseOnSurface,
|
||||
"error" to colors.error,
|
||||
"onError" to colors.onError,
|
||||
"errorContainer" to colors.errorContainer,
|
||||
"onErrorContainer" to colors.onErrorContainer,
|
||||
"outline" to colors.outline,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** The screen that shows all the Android colors. */
|
||||
@Composable
|
||||
fun AndroidColorsScreen() {
|
||||
val colors = LocalAndroidColorScheme.current
|
||||
ColorsScreen(
|
||||
listOf(
|
||||
"colorPrimary" to colors.colorPrimary,
|
||||
"colorPrimaryDark" to colors.colorPrimaryDark,
|
||||
"colorAccent" to colors.colorAccent,
|
||||
"colorAccentPrimary" to colors.colorAccentPrimary,
|
||||
"colorAccentSecondary" to colors.colorAccentSecondary,
|
||||
"colorAccentTertiary" to colors.colorAccentTertiary,
|
||||
"colorAccentPrimaryVariant" to colors.colorAccentPrimaryVariant,
|
||||
"colorAccentSecondaryVariant" to colors.colorAccentSecondaryVariant,
|
||||
"colorAccentTertiaryVariant" to colors.colorAccentTertiaryVariant,
|
||||
"colorSurface" to colors.colorSurface,
|
||||
"colorSurfaceHighlight" to colors.colorSurfaceHighlight,
|
||||
"colorSurfaceVariant" to colors.colorSurfaceVariant,
|
||||
"colorSurfaceHeader" to colors.colorSurfaceHeader,
|
||||
"colorError" to colors.colorError,
|
||||
"colorBackground" to colors.colorBackground,
|
||||
"colorBackgroundFloating" to colors.colorBackgroundFloating,
|
||||
"panelColorBackground" to colors.panelColorBackground,
|
||||
"textColorPrimary" to colors.textColorPrimary,
|
||||
"textColorSecondary" to colors.textColorSecondary,
|
||||
"textColorTertiary" to colors.textColorTertiary,
|
||||
"textColorPrimaryInverse" to colors.textColorPrimaryInverse,
|
||||
"textColorSecondaryInverse" to colors.textColorSecondaryInverse,
|
||||
"textColorTertiaryInverse" to colors.textColorTertiaryInverse,
|
||||
"textColorOnAccent" to colors.textColorOnAccent,
|
||||
"colorForeground" to colors.colorForeground,
|
||||
"colorForegroundInverse" to colors.colorForegroundInverse,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColorsScreen(
|
||||
colors: List<Pair<String, Color>>,
|
||||
) {
|
||||
LazyColumn(
|
||||
Modifier.fillMaxWidth(),
|
||||
) {
|
||||
colors.forEach { (name, color) -> item { ColorTile(color, name) } }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColorTile(
|
||||
color: Color,
|
||||
name: String,
|
||||
) {
|
||||
Row(
|
||||
Modifier.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
val shape = RoundedCornerShape(16.dp)
|
||||
Spacer(
|
||||
Modifier.border(1.dp, MaterialTheme.colorScheme.onBackground, shape)
|
||||
.background(color, shape)
|
||||
.size(64.dp)
|
||||
)
|
||||
Spacer(Modifier.width(16.dp))
|
||||
Text(name)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
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
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.DarkMode
|
||||
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.InvertColors
|
||||
import androidx.compose.material.icons.filled.LightMode
|
||||
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),
|
||||
Normal(1f),
|
||||
Big(1.15f),
|
||||
Bigger(1.30f),
|
||||
}
|
||||
|
||||
/** A configuration panel that allows to toggle the theme, font scale and layout direction. */
|
||||
@Composable
|
||||
fun ConfigurationControls(
|
||||
theme: Theme,
|
||||
fontScale: FontScale,
|
||||
layoutDirection: LayoutDirection,
|
||||
onChangeTheme: () -> Unit,
|
||||
onChangeLayoutDirection: () -> Unit,
|
||||
onChangeFontScale: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
// 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(modifier) {
|
||||
// Dark/light theme.
|
||||
item {
|
||||
TextButton(onChangeTheme) {
|
||||
val text: String
|
||||
val icon: ImageVector
|
||||
|
||||
when (theme) {
|
||||
Theme.System -> {
|
||||
icon = Icons.Default.InvertColors
|
||||
text = "System"
|
||||
}
|
||||
Theme.Dark -> {
|
||||
icon = Icons.Default.DarkMode
|
||||
text = "Dark"
|
||||
}
|
||||
Theme.Light -> {
|
||||
icon = Icons.Default.LightMode
|
||||
text = "Light"
|
||||
}
|
||||
}
|
||||
|
||||
Icon(icon, null)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(text)
|
||||
}
|
||||
}
|
||||
|
||||
// Font scale.
|
||||
item {
|
||||
TextButton(onChangeFontScale) {
|
||||
Icon(Icons.Default.FormatSize, null)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
|
||||
Text(fontScale.name)
|
||||
}
|
||||
}
|
||||
|
||||
// Layout direction.
|
||||
item {
|
||||
TextButton(onChangeLayoutDirection) {
|
||||
when (layoutDirection) {
|
||||
LayoutDirection.Ltr -> {
|
||||
Icon(Icons.Default.FormatTextdirectionLToR, null)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text("LTR")
|
||||
}
|
||||
LayoutDirection.Rtl -> {
|
||||
Icon(Icons.Default.FormatTextdirectionRToL, null)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text("RTL")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.systemui.compose.gallery
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.android.systemui.ExampleFeature
|
||||
|
||||
/** The screen that shows ExampleFeature. */
|
||||
@Composable
|
||||
fun ExampleFeatureScreen(modifier: Modifier = Modifier) {
|
||||
Column(modifier) { ExampleFeature("This is an example feature!") }
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.systemui.compose.gallery
|
||||
|
||||
import android.app.UiModeManager
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.runtime.SideEffect
|
||||
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.graphics.Color
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.android.systemui.compose.rememberSystemUiController
|
||||
|
||||
class GalleryActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
val uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
|
||||
|
||||
setContent {
|
||||
var theme by rememberSaveable { mutableStateOf(Theme.System) }
|
||||
val onChangeTheme = {
|
||||
// Change to the next theme for a toggle behavior.
|
||||
theme =
|
||||
when (theme) {
|
||||
Theme.System -> Theme.Dark
|
||||
Theme.Dark -> Theme.Light
|
||||
Theme.Light -> Theme.System
|
||||
}
|
||||
}
|
||||
|
||||
val isSystemInDarkTheme = isSystemInDarkTheme()
|
||||
val isDark = theme == Theme.Dark || (theme == Theme.System && isSystemInDarkTheme)
|
||||
val useDarkIcons = !isDark
|
||||
val systemUiController = rememberSystemUiController()
|
||||
SideEffect {
|
||||
systemUiController.setSystemBarsColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = useDarkIcons,
|
||||
)
|
||||
|
||||
uiModeManager.setApplicationNightMode(
|
||||
when (theme) {
|
||||
Theme.System -> UiModeManager.MODE_NIGHT_AUTO
|
||||
Theme.Dark -> UiModeManager.MODE_NIGHT_YES
|
||||
Theme.Light -> UiModeManager.MODE_NIGHT_NO
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
GalleryApp(theme, onChangeTheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class Theme {
|
||||
System,
|
||||
Dark,
|
||||
Light,
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.android.systemui.compose.gallery
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.android.systemui.compose.theme.SystemUITheme
|
||||
|
||||
/** The gallery app screens. */
|
||||
object GalleryAppScreens {
|
||||
val Typography = ChildScreen("typography") { TypographyScreen() }
|
||||
val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() }
|
||||
val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() }
|
||||
val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() }
|
||||
|
||||
val Home =
|
||||
ParentScreen(
|
||||
"home",
|
||||
mapOf(
|
||||
"Typography" to Typography,
|
||||
"Material colors" to MaterialColors,
|
||||
"Android colors" to AndroidColors,
|
||||
"Example feature" to ExampleFeature,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** The main content of the app, that shows [GalleryAppScreens.Home] by default. */
|
||||
@Composable
|
||||
private fun MainContent() {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
val navController = rememberNavController()
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = GalleryAppScreens.Home.identifier,
|
||||
) {
|
||||
screen(GalleryAppScreens.Home, navController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The top-level composable shown when starting the app. This composable always shows a
|
||||
* [ConfigurationControls] at the top of the screen, above the [MainContent].
|
||||
*/
|
||||
@Composable
|
||||
fun GalleryApp(
|
||||
theme: Theme,
|
||||
onChangeTheme: () -> Unit,
|
||||
) {
|
||||
val systemFontScale = LocalDensity.current.fontScale
|
||||
var fontScale: FontScale by remember {
|
||||
mutableStateOf(
|
||||
FontScale.values().firstOrNull { it.scale == systemFontScale } ?: FontScale.Normal
|
||||
)
|
||||
}
|
||||
val context = LocalContext.current
|
||||
val density = Density(context.resources.displayMetrics.density, fontScale.scale)
|
||||
val onChangeFontScale = {
|
||||
fontScale =
|
||||
when (fontScale) {
|
||||
FontScale.Small -> FontScale.Normal
|
||||
FontScale.Normal -> FontScale.Big
|
||||
FontScale.Big -> FontScale.Bigger
|
||||
FontScale.Bigger -> FontScale.Small
|
||||
}
|
||||
}
|
||||
|
||||
val systemLayoutDirection = LocalLayoutDirection.current
|
||||
var layoutDirection by remember { mutableStateOf(systemLayoutDirection) }
|
||||
val onChangeLayoutDirection = {
|
||||
layoutDirection =
|
||||
when (layoutDirection) {
|
||||
LayoutDirection.Ltr -> LayoutDirection.Rtl
|
||||
LayoutDirection.Rtl -> LayoutDirection.Ltr
|
||||
}
|
||||
}
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalDensity provides density,
|
||||
LocalLayoutDirection provides layoutDirection,
|
||||
) {
|
||||
SystemUITheme {
|
||||
Surface(
|
||||
Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
Column(Modifier.fillMaxSize().systemBarsPadding().padding(16.dp)) {
|
||||
ConfigurationControls(
|
||||
theme,
|
||||
fontScale,
|
||||
layoutDirection,
|
||||
onChangeTheme,
|
||||
onChangeLayoutDirection,
|
||||
onChangeFontScale,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(4.dp))
|
||||
|
||||
MainContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.systemui.compose.gallery
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.navigation
|
||||
|
||||
/**
|
||||
* A screen in an app. It is either an [ParentScreen] which lists its child screens to navigate to
|
||||
* them or a [ChildScreen] which shows some content.
|
||||
*/
|
||||
sealed class Screen(val identifier: String)
|
||||
|
||||
class ParentScreen(
|
||||
identifier: String,
|
||||
val children: Map<String, Screen>,
|
||||
) : Screen(identifier)
|
||||
|
||||
class ChildScreen(
|
||||
identifier: String,
|
||||
val content: @Composable (NavController) -> Unit,
|
||||
) : Screen(identifier)
|
||||
|
||||
/** Create the navigation graph for [screen]. */
|
||||
fun NavGraphBuilder.screen(screen: Screen, navController: NavController) {
|
||||
when (screen) {
|
||||
is ChildScreen -> composable(screen.identifier) { screen.content(navController) }
|
||||
is ParentScreen -> {
|
||||
val menuRoute = "${screen.identifier}_menu"
|
||||
navigation(startDestination = menuRoute, route = screen.identifier) {
|
||||
// The menu to navigate to one of the children screens.
|
||||
composable(menuRoute) { ScreenMenu(screen, navController) }
|
||||
|
||||
// The content of the child screens.
|
||||
screen.children.forEach { (_, child) -> screen(child, navController) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenMenu(
|
||||
screen: ParentScreen,
|
||||
navController: NavController,
|
||||
) {
|
||||
LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
screen.children.forEach { (name, child) ->
|
||||
item {
|
||||
Surface(
|
||||
Modifier.fillMaxWidth(),
|
||||
color = MaterialTheme.colorScheme.secondaryContainer,
|
||||
shape = CircleShape,
|
||||
) {
|
||||
Column(
|
||||
Modifier.clickable { navController.navigate(child.identifier) }
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.systemui.compose.gallery
|
||||
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
||||
/** The screen that shows the Material text styles. */
|
||||
@Composable
|
||||
fun TypographyScreen() {
|
||||
val typography = MaterialTheme.typography
|
||||
|
||||
Column(
|
||||
Modifier.fillMaxSize()
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
FontLine("displayLarge", typography.displayLarge)
|
||||
FontLine("displayMedium", typography.displayMedium)
|
||||
FontLine("displaySmall", typography.displaySmall)
|
||||
FontLine("headlineLarge", typography.headlineLarge)
|
||||
FontLine("headlineMedium", typography.headlineMedium)
|
||||
FontLine("headlineSmall", typography.headlineSmall)
|
||||
FontLine("titleLarge", typography.titleLarge)
|
||||
FontLine("titleMedium", typography.titleMedium)
|
||||
FontLine("titleSmall", typography.titleSmall)
|
||||
FontLine("bodyLarge", typography.bodyLarge)
|
||||
FontLine("bodyMedium", typography.bodyMedium)
|
||||
FontLine("bodySmall", typography.bodySmall)
|
||||
FontLine("labelLarge", typography.labelLarge)
|
||||
FontLine("labelMedium", typography.labelMedium)
|
||||
FontLine("labelSmall", typography.labelSmall)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FontLine(name: String, style: TextStyle) {
|
||||
Text(
|
||||
"$name (${style.fontSize}/${style.lineHeight}, W${style.fontWeight?.weight})",
|
||||
style = style,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Visible,
|
||||
)
|
||||
}
|
||||
47
packages/SystemUI/compose/gallery/tests/Android.bp
Normal file
@@ -0,0 +1,47 @@
|
||||
// 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 {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
|
||||
}
|
||||
|
||||
android_test {
|
||||
name: "SystemUIComposeGalleryTests",
|
||||
manifest: "AndroidManifest.xml",
|
||||
test_suites: ["device-tests"],
|
||||
sdk_version: "current",
|
||||
certificate: "platform",
|
||||
|
||||
srcs: [
|
||||
"src/**/*.kt",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"SystemUIComposeGalleryLib",
|
||||
|
||||
"androidx.test.runner",
|
||||
"androidx.test.ext.junit",
|
||||
|
||||
"androidx.compose.runtime_runtime",
|
||||
"androidx.compose.ui_ui-test-junit4",
|
||||
"androidx.compose.ui_ui-test-manifest",
|
||||
],
|
||||
|
||||
kotlincflags: ["-Xjvm-default=enable"],
|
||||
}
|
||||
28
packages/SystemUI/compose/gallery/tests/AndroidManifest.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.systemui.compose.gallery.tests" >
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.systemui.compose.gallery.tests"
|
||||
android:label="Tests for SystemUIComposeGallery"/>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.systemui.compose.gallery
|
||||
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.systemui.compose.theme.SystemUITheme
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ScreenshotsTests {
|
||||
@get:Rule val composeRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun exampleFeatureScreenshotTest() {
|
||||
// TODO(b/230832101): Wire this with the screenshot diff testing infra. We should reuse the
|
||||
// configuration of the features in the gallery app to populate the UIs.
|
||||
composeRule.setContent { SystemUITheme { ExampleFeatureScreen() } }
|
||||
}
|
||||
}
|
||||
43
packages/SystemUI/compose/testing/Android.bp
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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 {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
|
||||
}
|
||||
|
||||
android_library {
|
||||
name: "SystemUIComposeTesting",
|
||||
manifest: "AndroidManifest.xml",
|
||||
|
||||
srcs: [
|
||||
"src/**/*.kt",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"SystemUIComposeCore",
|
||||
"SystemUIScreenshotLib",
|
||||
|
||||
"androidx.compose.runtime_runtime",
|
||||
"androidx.compose.material3_material3",
|
||||
"androidx.compose.ui_ui-test-junit4",
|
||||
"androidx.compose.ui_ui-test-manifest",
|
||||
],
|
||||
|
||||
kotlincflags: ["-Xjvm-default=all"],
|
||||
}
|
||||
25
packages/SystemUI/compose/testing/AndroidManifest.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.android.systemui.testing.compose">
|
||||
<application
|
||||
android:appComponentFactory="androidx.core.app.AppComponentFactory"
|
||||
tools:replace="android:appComponentFactory">
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.systemui.testing.compose
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.ViewRootForTest
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onRoot
|
||||
import com.android.systemui.compose.theme.SystemUITheme
|
||||
import com.android.systemui.testing.screenshot.ScreenshotActivity
|
||||
import com.android.systemui.testing.screenshot.SystemUIGoldenImagePathManager
|
||||
import com.android.systemui.testing.screenshot.UnitTestBitmapMatcher
|
||||
import com.android.systemui.testing.screenshot.drawIntoBitmap
|
||||
import org.junit.rules.RuleChain
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
import platform.test.screenshot.DeviceEmulationRule
|
||||
import platform.test.screenshot.DeviceEmulationSpec
|
||||
import platform.test.screenshot.MaterialYouColorsRule
|
||||
import platform.test.screenshot.ScreenshotTestRule
|
||||
import platform.test.screenshot.getEmulatedDevicePathConfig
|
||||
|
||||
/** A rule for Compose screenshot diff tests. */
|
||||
class ComposeScreenshotTestRule(emulationSpec: DeviceEmulationSpec) : TestRule {
|
||||
private val colorsRule = MaterialYouColorsRule()
|
||||
private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
|
||||
private val screenshotRule =
|
||||
ScreenshotTestRule(
|
||||
SystemUIGoldenImagePathManager(getEmulatedDevicePathConfig(emulationSpec))
|
||||
)
|
||||
private val composeRule = createAndroidComposeRule<ScreenshotActivity>()
|
||||
private val delegateRule =
|
||||
RuleChain.outerRule(colorsRule)
|
||||
.around(deviceEmulationRule)
|
||||
.around(screenshotRule)
|
||||
.around(composeRule)
|
||||
private val matcher = UnitTestBitmapMatcher
|
||||
|
||||
override fun apply(base: Statement, description: Description): Statement {
|
||||
return delegateRule.apply(base, description)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare [content] with the golden image identified by [goldenIdentifier] in the context of
|
||||
* [testSpec].
|
||||
*/
|
||||
fun screenshotTest(
|
||||
goldenIdentifier: String,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
// Make sure that the activity draws full screen and fits the whole display instead of the
|
||||
// system bars.
|
||||
val activity = composeRule.activity
|
||||
activity.mainExecutor.execute { activity.window.setDecorFitsSystemWindows(false) }
|
||||
|
||||
// Set the content using the AndroidComposeRule to make sure that the Activity is set up
|
||||
// correctly.
|
||||
composeRule.setContent {
|
||||
SystemUITheme {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
composeRule.waitForIdle()
|
||||
|
||||
val view = (composeRule.onRoot().fetchSemanticsNode().root as ViewRootForTest).view
|
||||
screenshotRule.assertBitmapAgainstGolden(view.drawIntoBitmap(), goldenIdentifier, matcher)
|
||||
}
|
||||
}
|
||||