1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.settingslib.spa.widget.preference
18
19 import androidx.compose.foundation.background
20 import androidx.compose.foundation.layout.Box
21 import androidx.compose.foundation.layout.Column
22 import androidx.compose.foundation.layout.Row
23 import androidx.compose.foundation.layout.Spacer
24 import androidx.compose.foundation.layout.fillMaxWidth
25 import androidx.compose.foundation.layout.heightIn
26 import androidx.compose.foundation.layout.padding
27 import androidx.compose.foundation.layout.size
28 import androidx.compose.foundation.layout.width
29 import androidx.compose.material3.HorizontalDivider
30 import androidx.compose.material3.MaterialTheme
31 import androidx.compose.runtime.Composable
32 import androidx.compose.ui.Alignment
33 import androidx.compose.ui.Modifier
34 import androidx.compose.ui.draw.clip
35 import androidx.compose.ui.semantics.semantics
36 import androidx.compose.ui.tooling.preview.Preview
37 import androidx.compose.ui.unit.Dp
38 import androidx.compose.ui.unit.dp
39 import com.android.settingslib.spa.framework.compose.thenIf
40 import com.android.settingslib.spa.framework.theme.SettingsDimension
41 import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled
42 import com.android.settingslib.spa.framework.theme.SettingsShape
43 import com.android.settingslib.spa.framework.theme.SettingsTheme
44 import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
45 import com.android.settingslib.spa.widget.ui.LocalIsInCategory
46 import com.android.settingslib.spa.widget.ui.SettingsTitle
47
48 @Composable
BaseLayoutnull49 internal fun BaseLayout(
50 title: String,
51 subTitle: @Composable () -> Unit,
52 modifier: Modifier = Modifier,
53 titleContentDescription: String? = null,
54 icon: @Composable (() -> Unit)? = null,
55 enabled: () -> Boolean = { true },
56 paddingStart: Dp = SettingsDimension.itemPaddingStart,
57 paddingEnd: Dp = SettingsDimension.itemPaddingEnd,
58 paddingVertical: Dp = SettingsDimension.itemPaddingVertical,
<lambda>null59 widget: @Composable () -> Unit = {},
60 ) {
61 val surfaceBright = MaterialTheme.colorScheme.surfaceBright
62 Row(
63 modifier =
64 modifier
65 .fillMaxWidth()
<lambda>null66 .semantics(mergeDescendants = true) {}
<lambda>null67 .thenIf(isSpaExpressiveEnabled) {
68 Modifier.heightIn(min = SettingsDimension.preferenceMinHeight)
69 }
<lambda>null70 .thenIf(isSpaExpressiveEnabled && LocalIsInCategory.current) {
71 Modifier.clip(SettingsShape.CornerExtraSmall).background(surfaceBright)
72 }
73 .padding(end = paddingEnd),
74 verticalAlignment = Alignment.CenterVertically,
<lambda>null75 ) {
76 val alphaModifier = Modifier.alphaForEnabled(enabled())
77 BaseIcon(icon, alphaModifier, paddingStart)
78 Titles(
79 title = title,
80 titleContentDescription = titleContentDescription,
81 subTitle = subTitle,
82 modifier = alphaModifier.weight(1f).padding(vertical = paddingVertical),
83 )
84 widget()
85 }
86 }
87
88 @Composable
BaseIconnull89 internal fun BaseIcon(icon: @Composable (() -> Unit)?, modifier: Modifier, paddingStart: Dp) {
90 if (isSpaExpressiveEnabled) {
91 Spacer(modifier = Modifier.width(width = paddingStart))
92 if (icon != null) {
93 Box(
94 modifier = modifier.size(SettingsDimension.itemIconContainerSizeSmall),
95 contentAlignment = Alignment.Center,
96 ) {
97 icon()
98 }
99 Spacer(modifier = Modifier.width(width = SettingsDimension.paddingExtraSmall6))
100 }
101 } else {
102 if (icon != null) {
103 Box(
104 modifier = modifier.size(SettingsDimension.itemIconContainerSize),
105 contentAlignment = Alignment.Center,
106 ) {
107 icon()
108 }
109 } else {
110 Spacer(modifier = Modifier.width(width = paddingStart))
111 }
112 }
113 }
114
115 // Extracts a scope to avoid frequent recompose outside scope.
116 @Composable
Titlesnull117 private fun Titles(
118 title: String,
119 titleContentDescription: String?,
120 subTitle: @Composable () -> Unit,
121 modifier: Modifier,
122 ) {
123 Column(modifier) {
124 SettingsTitle(title, titleContentDescription)
125 subTitle()
126 }
127 }
128
129 @Preview
130 @Composable
BaseLayoutPreviewnull131 private fun BaseLayoutPreview() {
132 SettingsTheme {
133 BaseLayout(title = "Title", subTitle = { HorizontalDivider(thickness = 10.dp) })
134 }
135 }
136