Merge changes I90d871bb,If284f43c into tm-qpr-dev
* changes: UserSwitcher in Compose. Adds Compose buttons.
This commit is contained in:
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* 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 androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.ButtonColors
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.android.systemui.compose.theme.LocalAndroidColorScheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SysUiButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
content: @Composable RowScope.() -> Unit,
|
||||||
|
) {
|
||||||
|
androidx.compose.material3.Button(
|
||||||
|
modifier = modifier.padding(vertical = 6.dp).height(36.dp),
|
||||||
|
colors = filledButtonColors(),
|
||||||
|
contentPadding = ButtonPaddings,
|
||||||
|
onClick = onClick,
|
||||||
|
enabled = enabled,
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SysUiOutlinedButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
content: @Composable RowScope.() -> Unit,
|
||||||
|
) {
|
||||||
|
androidx.compose.material3.OutlinedButton(
|
||||||
|
modifier = modifier.padding(vertical = 6.dp).height(36.dp),
|
||||||
|
enabled = enabled,
|
||||||
|
colors = outlineButtonColors(),
|
||||||
|
border = outlineButtonBorder(),
|
||||||
|
contentPadding = ButtonPaddings,
|
||||||
|
onClick = onClick,
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SysUiTextButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
content: @Composable RowScope.() -> Unit,
|
||||||
|
) {
|
||||||
|
androidx.compose.material3.TextButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ButtonPaddings = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun filledButtonColors(): ButtonColors {
|
||||||
|
val colors = LocalAndroidColorScheme.current
|
||||||
|
return ButtonDefaults.buttonColors(
|
||||||
|
containerColor = colors.colorAccentPrimary,
|
||||||
|
contentColor = colors.textColorOnAccent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun outlineButtonColors(): ButtonColors {
|
||||||
|
val colors = LocalAndroidColorScheme.current
|
||||||
|
return ButtonDefaults.outlinedButtonColors(
|
||||||
|
contentColor = colors.textColorPrimary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun outlineButtonBorder(): BorderStroke {
|
||||||
|
val colors = LocalAndroidColorScheme.current
|
||||||
|
return BorderStroke(
|
||||||
|
width = 1.dp,
|
||||||
|
color = colors.colorAccentPrimaryVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.ui.compose
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import com.android.systemui.common.shared.model.Text
|
||||||
|
|
||||||
|
/** Returns the loaded [String] or `null` if there isn't one. */
|
||||||
|
@Composable
|
||||||
|
fun Text.load(): String? {
|
||||||
|
return when (this) {
|
||||||
|
is Text.Loaded -> text
|
||||||
|
is Text.Resource -> stringResource(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
/*
|
||||||
|
* 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.user.ui.compose
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.sizeIn
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.painter.ColorPainter
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.res.colorResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.android.systemui.common.ui.compose.load
|
||||||
|
import com.android.systemui.compose.SysUiOutlinedButton
|
||||||
|
import com.android.systemui.compose.SysUiTextButton
|
||||||
|
import com.android.systemui.compose.features.R
|
||||||
|
import com.android.systemui.compose.theme.LocalAndroidColorScheme
|
||||||
|
import com.android.systemui.user.ui.viewmodel.UserActionViewModel
|
||||||
|
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
|
||||||
|
import com.android.systemui.user.ui.viewmodel.UserViewModel
|
||||||
|
import java.lang.Integer.min
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UserSwitcherScreen(
|
||||||
|
viewModel: UserSwitcherViewModel,
|
||||||
|
onFinished: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val isFinishRequested: Boolean by viewModel.isFinishRequested.collectAsState(false)
|
||||||
|
val users: List<UserViewModel> by viewModel.users.collectAsState(emptyList())
|
||||||
|
val maxUserColumns: Int by viewModel.maximumUserColumns.collectAsState(1)
|
||||||
|
val menuActions: List<UserActionViewModel> by viewModel.menu.collectAsState(emptyList())
|
||||||
|
val isOpenMenuButtonVisible: Boolean by viewModel.isOpenMenuButtonVisible.collectAsState(false)
|
||||||
|
val isMenuVisible: Boolean by viewModel.isMenuVisible.collectAsState(false)
|
||||||
|
|
||||||
|
UserSwitcherScreenStateless(
|
||||||
|
isFinishRequested = isFinishRequested,
|
||||||
|
users = users,
|
||||||
|
maxUserColumns = maxUserColumns,
|
||||||
|
menuActions = menuActions,
|
||||||
|
isOpenMenuButtonVisible = isOpenMenuButtonVisible,
|
||||||
|
isMenuVisible = isMenuVisible,
|
||||||
|
onMenuClosed = viewModel::onMenuClosed,
|
||||||
|
onOpenMenuButtonClicked = viewModel::onOpenMenuButtonClicked,
|
||||||
|
onCancelButtonClicked = viewModel::onCancelButtonClicked,
|
||||||
|
onFinished = {
|
||||||
|
onFinished()
|
||||||
|
viewModel.onFinished()
|
||||||
|
},
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun UserSwitcherScreenStateless(
|
||||||
|
isFinishRequested: Boolean,
|
||||||
|
users: List<UserViewModel>,
|
||||||
|
maxUserColumns: Int,
|
||||||
|
menuActions: List<UserActionViewModel>,
|
||||||
|
isOpenMenuButtonVisible: Boolean,
|
||||||
|
isMenuVisible: Boolean,
|
||||||
|
onMenuClosed: () -> Unit,
|
||||||
|
onOpenMenuButtonClicked: () -> Unit,
|
||||||
|
onCancelButtonClicked: () -> Unit,
|
||||||
|
onFinished: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
LaunchedEffect(isFinishRequested) {
|
||||||
|
if (isFinishRequested) {
|
||||||
|
onFinished()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier =
|
||||||
|
modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(
|
||||||
|
horizontal = 60.dp,
|
||||||
|
vertical = 40.dp,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
UserGrid(
|
||||||
|
users = users,
|
||||||
|
maxUserColumns = maxUserColumns,
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
)
|
||||||
|
|
||||||
|
Buttons(
|
||||||
|
menuActions = menuActions,
|
||||||
|
isOpenMenuButtonVisible = isOpenMenuButtonVisible,
|
||||||
|
isMenuVisible = isMenuVisible,
|
||||||
|
onMenuClosed = onMenuClosed,
|
||||||
|
onOpenMenuButtonClicked = onOpenMenuButtonClicked,
|
||||||
|
onCancelButtonClicked = onCancelButtonClicked,
|
||||||
|
modifier = Modifier.align(Alignment.BottomEnd),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun UserGrid(
|
||||||
|
users: List<UserViewModel>,
|
||||||
|
maxUserColumns: Int,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(44.dp),
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
val rowCount = ceil(users.size / maxUserColumns.toFloat()).toInt()
|
||||||
|
(0 until rowCount).forEach { rowIndex ->
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(64.dp),
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
val fromIndex = rowIndex * maxUserColumns
|
||||||
|
val toIndex = min(users.size, (rowIndex + 1) * maxUserColumns)
|
||||||
|
users.subList(fromIndex, toIndex).forEach { user ->
|
||||||
|
UserItem(
|
||||||
|
viewModel = user,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun UserItem(
|
||||||
|
viewModel: UserViewModel,
|
||||||
|
) {
|
||||||
|
val onClicked = viewModel.onClicked
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier =
|
||||||
|
if (onClicked != null) {
|
||||||
|
Modifier.clickable { onClicked() }
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
}
|
||||||
|
.alpha(viewModel.alpha),
|
||||||
|
) {
|
||||||
|
Box {
|
||||||
|
UserItemBackground(modifier = Modifier.align(Alignment.Center).size(222.dp))
|
||||||
|
|
||||||
|
UserItemIcon(
|
||||||
|
image = viewModel.image,
|
||||||
|
isSelectionMarkerVisible = viewModel.isSelectionMarkerVisible,
|
||||||
|
modifier = Modifier.align(Alignment.Center).size(222.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// User name
|
||||||
|
val text = viewModel.name.load()
|
||||||
|
if (text != null) {
|
||||||
|
// We use the box to center-align the text vertically as that is not possible with Text
|
||||||
|
// alone.
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.size(width = 222.dp, height = 48.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
color = colorResource(com.android.internal.R.color.system_neutral1_50),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun UserItemBackground(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = ColorPainter(LocalAndroidColorScheme.current.colorBackground),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = modifier.clip(CircleShape),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun UserItemIcon(
|
||||||
|
image: Drawable,
|
||||||
|
isSelectionMarkerVisible: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
bitmap = image.toBitmap().asImageBitmap(),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier =
|
||||||
|
if (isSelectionMarkerVisible) {
|
||||||
|
// Draws a ring
|
||||||
|
modifier.border(
|
||||||
|
width = 8.dp,
|
||||||
|
color = LocalAndroidColorScheme.current.colorAccentPrimary,
|
||||||
|
shape = CircleShape,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
modifier
|
||||||
|
}
|
||||||
|
.padding(16.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Buttons(
|
||||||
|
menuActions: List<UserActionViewModel>,
|
||||||
|
isOpenMenuButtonVisible: Boolean,
|
||||||
|
isMenuVisible: Boolean,
|
||||||
|
onMenuClosed: () -> Unit,
|
||||||
|
onOpenMenuButtonClicked: () -> Unit,
|
||||||
|
onCancelButtonClicked: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
// Cancel button.
|
||||||
|
SysUiTextButton(
|
||||||
|
onClick = onCancelButtonClicked,
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.cancel))
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Open menu" button.
|
||||||
|
if (isOpenMenuButtonVisible) {
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
// To properly use a DropdownMenu in Compose, we need to wrap the button that opens it
|
||||||
|
// and the menu itself in a Box.
|
||||||
|
Box {
|
||||||
|
SysUiOutlinedButton(
|
||||||
|
onClick = onOpenMenuButtonClicked,
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.add))
|
||||||
|
}
|
||||||
|
Menu(
|
||||||
|
viewModel = menuActions,
|
||||||
|
isMenuVisible = isMenuVisible,
|
||||||
|
onMenuClosed = onMenuClosed,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Menu(
|
||||||
|
viewModel: List<UserActionViewModel>,
|
||||||
|
isMenuVisible: Boolean,
|
||||||
|
onMenuClosed: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val maxItemWidth = LocalConfiguration.current.screenWidthDp.dp / 4
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = isMenuVisible,
|
||||||
|
onDismissRequest = onMenuClosed,
|
||||||
|
modifier =
|
||||||
|
modifier.background(
|
||||||
|
color = MaterialTheme.colorScheme.inverseOnSurface,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
viewModel.forEachIndexed { index, action ->
|
||||||
|
MenuItem(
|
||||||
|
viewModel = action,
|
||||||
|
onClicked = { action.onClicked() },
|
||||||
|
topPadding =
|
||||||
|
if (index == 0) {
|
||||||
|
16.dp
|
||||||
|
} else {
|
||||||
|
0.dp
|
||||||
|
},
|
||||||
|
bottomPadding =
|
||||||
|
if (index == viewModel.size - 1) {
|
||||||
|
16.dp
|
||||||
|
} else {
|
||||||
|
0.dp
|
||||||
|
},
|
||||||
|
modifier = Modifier.sizeIn(maxWidth = maxItemWidth),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MenuItem(
|
||||||
|
viewModel: UserActionViewModel,
|
||||||
|
onClicked: () -> Unit,
|
||||||
|
topPadding: Dp,
|
||||||
|
bottomPadding: Dp,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val density = LocalDensity.current
|
||||||
|
|
||||||
|
val icon =
|
||||||
|
remember(viewModel.iconResourceId) {
|
||||||
|
val drawable =
|
||||||
|
checkNotNull(AppCompatResources.getDrawable(context, viewModel.iconResourceId))
|
||||||
|
drawable
|
||||||
|
.toBitmap(
|
||||||
|
size = with(density) { 20.dp.toPx() }.toInt(),
|
||||||
|
tintColor = Color.White,
|
||||||
|
)
|
||||||
|
.asImageBitmap()
|
||||||
|
}
|
||||||
|
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(viewModel.textResourceId),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = onClicked,
|
||||||
|
leadingIcon = {
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
Image(
|
||||||
|
bitmap = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
modifier =
|
||||||
|
modifier
|
||||||
|
.heightIn(
|
||||||
|
min = 56.dp,
|
||||||
|
)
|
||||||
|
.padding(
|
||||||
|
start = 18.dp,
|
||||||
|
end = 65.dp,
|
||||||
|
top = topPadding,
|
||||||
|
bottom = bottomPadding,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the [Drawable] to a [Bitmap].
|
||||||
|
*
|
||||||
|
* Note that this is a relatively memory-heavy operation as it allocates a whole bitmap and draws
|
||||||
|
* the `Drawable` onto it. Use sparingly and with care.
|
||||||
|
*/
|
||||||
|
private fun Drawable.toBitmap(
|
||||||
|
size: Int? = null,
|
||||||
|
tintColor: Color? = null,
|
||||||
|
): Bitmap {
|
||||||
|
val bitmap =
|
||||||
|
if (intrinsicWidth <= 0 || intrinsicHeight <= 0) {
|
||||||
|
Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
|
||||||
|
} else {
|
||||||
|
Bitmap.createBitmap(
|
||||||
|
size ?: intrinsicWidth,
|
||||||
|
size ?: intrinsicHeight,
|
||||||
|
Bitmap.Config.ARGB_8888
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val canvas = Canvas(bitmap)
|
||||||
|
setBounds(0, 0, canvas.width, canvas.height)
|
||||||
|
if (tintColor != null) {
|
||||||
|
setTint(tintColor.toArgb())
|
||||||
|
}
|
||||||
|
draw(canvas)
|
||||||
|
return bitmap
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
|
||||||
|
package com.android.systemui.compose.gallery
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.android.systemui.compose.SysUiButton
|
||||||
|
import com.android.systemui.compose.SysUiOutlinedButton
|
||||||
|
import com.android.systemui.compose.SysUiTextButton
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ButtonsScreen(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
SysUiButton(
|
||||||
|
onClick = {},
|
||||||
|
) {
|
||||||
|
Text("SysUiButton")
|
||||||
|
}
|
||||||
|
|
||||||
|
SysUiButton(
|
||||||
|
onClick = {},
|
||||||
|
enabled = false,
|
||||||
|
) {
|
||||||
|
Text("SysUiButton - disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
SysUiOutlinedButton(
|
||||||
|
onClick = {},
|
||||||
|
) {
|
||||||
|
Text("SysUiOutlinedButton")
|
||||||
|
}
|
||||||
|
|
||||||
|
SysUiOutlinedButton(
|
||||||
|
onClick = {},
|
||||||
|
enabled = false,
|
||||||
|
) {
|
||||||
|
Text("SysUiOutlinedButton - disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
SysUiTextButton(
|
||||||
|
onClick = {},
|
||||||
|
) {
|
||||||
|
Text("SysUiTextButton")
|
||||||
|
}
|
||||||
|
|
||||||
|
SysUiTextButton(
|
||||||
|
onClick = {},
|
||||||
|
enabled = false,
|
||||||
|
) {
|
||||||
|
Text("SysUiTextButton - disabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@ object GalleryAppScreens {
|
|||||||
val Typography = ChildScreen("typography") { TypographyScreen() }
|
val Typography = ChildScreen("typography") { TypographyScreen() }
|
||||||
val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() }
|
val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() }
|
||||||
val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() }
|
val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() }
|
||||||
|
val Buttons = ChildScreen("buttons") { ButtonsScreen() }
|
||||||
val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() }
|
val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() }
|
||||||
|
|
||||||
val PeopleEmpty =
|
val PeopleEmpty =
|
||||||
@@ -63,6 +64,7 @@ object GalleryAppScreens {
|
|||||||
"Material colors" to MaterialColors,
|
"Material colors" to MaterialColors,
|
||||||
"Android colors" to AndroidColors,
|
"Android colors" to AndroidColors,
|
||||||
"Example feature" to ExampleFeature,
|
"Example feature" to ExampleFeature,
|
||||||
|
"Buttons" to Buttons,
|
||||||
"People" to People,
|
"People" to People,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user