summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chaohui Wang <chaohuiw@google.com> 2023-10-17 12:39:50 +0800
committer Chaohui Wang <chaohuiw@google.com> 2023-11-08 02:05:36 +0000
commit09d46e05278f35e38171962b025dd474e48354aa (patch)
tree01deacc487527ae3d71bb0f0cc4e9da670328907
parent7c10e57ce5410b7d1c334512ac4394bd9557ba6f (diff)
[Spa] New Card widget
With icon, title, text and buttons. Bug: 305856149 Test: Run Gallery Test: unit test Change-Id: Idd6ab553646f0f9e9307b3a517b39098577244a2
-rw-r--r--packages/SettingsLib/Spa/gallery/res/values/strings.xml5
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt93
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt164
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt95
6 files changed, 361 insertions, 0 deletions
diff --git a/packages/SettingsLib/Spa/gallery/res/values/strings.xml b/packages/SettingsLib/Spa/gallery/res/values/strings.xml
index ec60f8c55331..18a6db035070 100644
--- a/packages/SettingsLib/Spa/gallery/res/values/strings.xml
+++ b/packages/SettingsLib/Spa/gallery/res/values/strings.xml
@@ -26,4 +26,9 @@
<string name="single_line_summary_preference_summary" translatable="false">A very long summary to show case a preference which only shows a single line summary.</string>
<!-- Footer text with two links. [DO NOT TRANSLATE] -->
<string name="footer_with_two_links" translatable="false">Annotated string with <a href="https://www.android.com/">link 1</a> and <a href="https://source.android.com/">link 2</a>.</string>
+
+ <!-- Sample title -->
+ <string name="sample_title" translatable="false">Lorem ipsum</string>
+ <!-- Sample text -->
+ <string name="sample_text" translatable="false">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent a rhoncus tellus. Nulla facilisi. Pellentesque erat ex, maximus viae turpis</string>
</resources>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 01596d2bc004..dd9335846e25 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -22,6 +22,7 @@ import com.android.settingslib.spa.framework.common.SettingsPageProviderReposito
import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
+import com.android.settingslib.spa.gallery.card.CardPageProvider
import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
@@ -96,6 +97,7 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
SettingsExposedDropdownMenuCheckBoxProvider,
SettingsTextFieldPasswordPageProvider,
SearchScaffoldPageProvider,
+ CardPageProvider,
),
rootPages = listOf(
HomePageProvider.createSettingsPage(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
new file mode 100644
index 000000000000..e914d5ce5140
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 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.settingslib.spa.gallery.card
+
+import android.os.Bundle
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.WarningAmber
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.gallery.R
+import com.android.settingslib.spa.widget.card.CardButton
+import com.android.settingslib.spa.widget.card.SettingsCard
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+object CardPageProvider : SettingsPageProvider {
+ override val name = "ActionButton"
+
+ override fun getTitle(arguments: Bundle?) = TITLE
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ RegularScaffold(title = TITLE) {
+ SettingsCardWithIcon()
+ SettingsCardWithoutIcon()
+ }
+ }
+
+ @Composable
+ private fun SettingsCardWithIcon() {
+ SettingsCard(
+ title = stringResource(R.string.sample_title),
+ text = stringResource(R.string.sample_text),
+ imageVector = Icons.Outlined.WarningAmber,
+ buttons = listOf(
+ CardButton(text = "Action") {},
+ CardButton(text = "Action", isMain = true) {},
+ )
+ )
+ }
+
+ @Composable
+ private fun SettingsCardWithoutIcon() {
+ SettingsCard(
+ title = stringResource(R.string.sample_title),
+ text = stringResource(R.string.sample_text),
+ buttons = listOf(
+ CardButton(text = "Action") {},
+ )
+ )
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = createSettingsPage())
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+
+ private const val TITLE = "Sample Card"
+}
+
+@Preview
+@Composable
+private fun CardPagePreview() {
+ SettingsTheme {
+ CardPageProvider.Page(null)
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index b339b4482137..f52ceec41253 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -28,6 +28,7 @@ import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
+import com.android.settingslib.spa.gallery.card.CardPageProvider
import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
@@ -69,6 +70,7 @@ object HomePageProvider : SettingsPageProvider {
ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
AlertDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
EditorMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ CardPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
new file mode 100644
index 000000000000..98873e037df8
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2023 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.settingslib.spa.widget.card
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.WarningAmber
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
+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.vector.ImageVector
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape.CornerExtraLarge
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.ui.SettingsBody
+import com.android.settingslib.spa.widget.ui.SettingsTitle
+
+data class CardButton(
+ val text: String,
+ val isMain: Boolean = false,
+ val onClick: () -> Unit,
+)
+
+@Composable
+fun SettingsCard(
+ title: String,
+ text: String,
+ imageVector: ImageVector? = null,
+ buttons: List<CardButton> = emptyList(),
+) {
+ Card(
+ shape = CornerExtraLarge,
+ colors = CardDefaults.cardColors(
+ containerColor = SettingsTheme.colorScheme.surface,
+ ),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(
+ horizontal = SettingsDimension.itemPaddingEnd,
+ vertical = SettingsDimension.itemPaddingAround,
+ ),
+ ) {
+ Column(
+ modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
+ verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
+ ) {
+ CardIcon(imageVector)
+ SettingsTitle(title)
+ SettingsBody(text)
+ Buttons(buttons)
+ }
+ }
+}
+
+@Composable
+private fun CardIcon(imageVector: ImageVector?) {
+ if (imageVector != null) {
+ Icon(
+ imageVector = imageVector,
+ contentDescription = null,
+ modifier = Modifier.size(SettingsDimension.itemIconSize),
+ tint = MaterialTheme.colorScheme.primary,
+ )
+ }
+}
+
+@Composable
+private fun Buttons(buttons: List<CardButton>) {
+ if (buttons.isNotEmpty()) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = SettingsDimension.itemPaddingAround),
+ horizontalArrangement = Arrangement.spacedBy(
+ space = SettingsDimension.itemPaddingEnd,
+ alignment = Alignment.End,
+ ),
+ ) {
+ for (button in buttons) {
+ Button(button)
+ }
+ }
+ }
+}
+
+@Composable
+private fun Button(button: CardButton) {
+ if (button.isMain) {
+ Button(
+ onClick = button.onClick,
+ colors = ButtonDefaults.buttonColors(
+ containerColor = SettingsTheme.colorScheme.primaryContainer,
+ ),
+ ) {
+ Text(
+ text = button.text,
+ color = SettingsTheme.colorScheme.onPrimaryContainer,
+ )
+ }
+ } else {
+ OutlinedButton(onClick = button.onClick) {
+ Text(
+ text = button.text,
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ }
+ }
+}
+
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Composable
+private fun SettingsCardPreviewLight() {
+ SettingsCardPreview()
+}
+
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun SettingsCardPreviewDark() {
+ SettingsCardPreview()
+}
+
+@Composable
+private fun SettingsCardPreview() {
+ SettingsTheme {
+ SettingsCard(
+ title = "Lorem ipsum",
+ text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+ imageVector = Icons.Outlined.WarningAmber,
+ buttons = listOf(
+ CardButton(text = "Action") {},
+ CardButton(text = "Action", isMain = true) {},
+ )
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
new file mode 100644
index 000000000000..0ec8507cb88b
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 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.settingslib.spa.widget.card
+
+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 com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsCardTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun settingsCard_titleDisplayed() {
+ composeTestRule.setContent {
+ SettingsCard(
+ title = TITLE,
+ text = "",
+ )
+ }
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun settingsCard_textDisplayed() {
+ composeTestRule.setContent {
+ SettingsCard(
+ title = "",
+ text = TEXT,
+ )
+ }
+
+ composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
+ }
+
+ @Test
+ fun settingsCard_buttonDisplayed() {
+ composeTestRule.setContent {
+ SettingsCard(
+ title = "",
+ text = "",
+ buttons = listOf(
+ CardButton(text = TEXT) {}
+ ),
+ )
+ }
+
+ composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
+ }
+
+ @Test
+ fun settingsCard_buttonCanBeClicked() {
+ var buttonClicked = false
+ composeTestRule.setContent {
+ SettingsCard(
+ title = "",
+ text = "",
+ buttons = listOf(
+ CardButton(text = TEXT) { buttonClicked = true }
+ ),
+ )
+ }
+
+ composeTestRule.onNodeWithText(TEXT).performClick()
+
+ assertThat(buttonClicked).isTrue()
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ const val TEXT = "Text"
+ }
+}