Merge "Create ActionButtons for SpaLib"
This commit is contained in:
@@ -19,6 +19,7 @@ package com.android.settingslib.spa.gallery
|
||||
import com.android.settingslib.spa.framework.common.SettingsEntryRepository
|
||||
import com.android.settingslib.spa.framework.common.SettingsPage
|
||||
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
|
||||
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
|
||||
import com.android.settingslib.spa.gallery.home.HomePageProvider
|
||||
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
|
||||
import com.android.settingslib.spa.gallery.page.FooterPageProvider
|
||||
@@ -51,6 +52,7 @@ object SpaEnvironment {
|
||||
FooterPageProvider,
|
||||
IllustrationPageProvider,
|
||||
CategoryPageProvider,
|
||||
ActionButtonPageProvider,
|
||||
),
|
||||
rootPages = listOf(
|
||||
SettingsPage.create(HomePageProvider.name)
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
package com.android.settingslib.spa.gallery.button
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Launch
|
||||
import androidx.compose.material.icons.outlined.WarningAmber
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
|
||||
import com.android.settingslib.spa.framework.common.SettingsPage
|
||||
import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
||||
import com.android.settingslib.spa.framework.compose.navigator
|
||||
import com.android.settingslib.spa.framework.theme.SettingsTheme
|
||||
import com.android.settingslib.spa.widget.button.ActionButton
|
||||
import com.android.settingslib.spa.widget.button.ActionButtons
|
||||
import com.android.settingslib.spa.widget.preference.Preference
|
||||
import com.android.settingslib.spa.widget.preference.PreferenceModel
|
||||
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
|
||||
|
||||
private const val TITLE = "Sample ActionButton"
|
||||
|
||||
object ActionButtonPageProvider : SettingsPageProvider {
|
||||
override val name = "ActionButton"
|
||||
|
||||
@Composable
|
||||
override fun Page(arguments: Bundle?) {
|
||||
ActionButtonPage()
|
||||
}
|
||||
|
||||
fun buildInjectEntry(): SettingsEntryBuilder {
|
||||
return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name))
|
||||
.setIsAllowSearch(true)
|
||||
.setUiLayoutFn {
|
||||
Preference(object : PreferenceModel {
|
||||
override val title = TITLE
|
||||
override val onClick = navigator(name)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ActionButtonPage() {
|
||||
RegularScaffold(title = TITLE) {
|
||||
val actionButtons = listOf(
|
||||
ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
|
||||
ActionButton(text = "Uninstall", imageVector = Icons.Outlined.Delete) {},
|
||||
ActionButton(text = "Force stop", imageVector = Icons.Outlined.WarningAmber) {},
|
||||
)
|
||||
ActionButtons(actionButtons)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun ActionButtonPagePreview() {
|
||||
SettingsTheme {
|
||||
ActionButtonPage()
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import com.android.settingslib.spa.framework.common.SettingsPage
|
||||
import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
||||
import com.android.settingslib.spa.framework.theme.SettingsTheme
|
||||
import com.android.settingslib.spa.gallery.R
|
||||
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
|
||||
import com.android.settingslib.spa.gallery.page.ArgumentPageModel
|
||||
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
|
||||
import com.android.settingslib.spa.gallery.page.FooterPageProvider
|
||||
@@ -50,6 +51,7 @@ object HomePageProvider : SettingsPageProvider {
|
||||
FooterPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
|
||||
IllustrationPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
|
||||
CategoryPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
|
||||
ActionButtonPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
@Composable
|
||||
@@ -40,3 +41,6 @@ internal fun materialColorScheme(isDarkTheme: Boolean): ColorScheme {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val ColorScheme.divider: Color
|
||||
get() = onSurface.copy(SettingsOpacity.Divider)
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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.settingslib.spa.widget.button
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Launch
|
||||
import androidx.compose.material.icons.outlined.WarningAmber
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
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.draw.clip
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.framework.theme.SettingsShape
|
||||
import com.android.settingslib.spa.framework.theme.SettingsTheme
|
||||
import com.android.settingslib.spa.framework.theme.divider
|
||||
|
||||
data class ActionButton(
|
||||
val text: String,
|
||||
val imageVector: ImageVector,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ActionButtons(actionButtons: List<ActionButton>) {
|
||||
Row(
|
||||
Modifier
|
||||
.padding(SettingsDimension.itemPaddingVertical)
|
||||
.clip(SettingsShape.CornerLarge)
|
||||
.height(IntrinsicSize.Min)
|
||||
) {
|
||||
for ((index, actionButton) in actionButtons.withIndex()) {
|
||||
if (index > 0) ButtonDivider()
|
||||
ActionButton(actionButton)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.ActionButton(actionButton: ActionButton) {
|
||||
FilledTonalButton(
|
||||
onClick = actionButton.onClick,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxHeight(),
|
||||
shape = RectangleShape,
|
||||
colors = ButtonDefaults.filledTonalButtonColors(
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
contentColor = SettingsTheme.colorScheme.categoryTitle,
|
||||
),
|
||||
contentPadding = PaddingValues(horizontal = 4.dp, vertical = 20.dp),
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = actionButton.imageVector,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(SettingsDimension.itemIconSize),
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = actionButton.text,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ButtonDivider() {
|
||||
Box(
|
||||
Modifier
|
||||
.width(1.dp)
|
||||
.background(color = MaterialTheme.colorScheme.divider)
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun ActionButtonsPreview() {
|
||||
SettingsTheme {
|
||||
ActionButtons(
|
||||
listOf(
|
||||
ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
|
||||
ActionButton(text = "Uninstall", imageVector = Icons.Outlined.Delete) {},
|
||||
ActionButton(text = "Force stop", imageVector = Icons.Outlined.WarningAmber) {},
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.framework.theme.SettingsOpacity
|
||||
import com.android.settingslib.spa.framework.theme.divider
|
||||
|
||||
@Composable
|
||||
internal fun TwoTargetPreference(
|
||||
@@ -67,6 +67,6 @@ private fun PreferenceDivider() {
|
||||
Modifier
|
||||
.padding(horizontal = SettingsDimension.itemPaddingEnd)
|
||||
.size(width = 1.dp, height = SettingsDimension.itemDividerHeight)
|
||||
.background(color = MaterialTheme.colorScheme.onSurface.copy(SettingsOpacity.Divider))
|
||||
.background(color = MaterialTheme.colorScheme.divider)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.settingslib.spa.widget.button
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Launch
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
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 ActionButtonsTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun button_displayed() {
|
||||
composeTestRule.setContent {
|
||||
ActionButtons(
|
||||
listOf(
|
||||
ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText("Open").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun button_clickable() {
|
||||
var clicked by mutableStateOf(false)
|
||||
composeTestRule.setContent {
|
||||
ActionButtons(
|
||||
listOf(
|
||||
ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {
|
||||
clicked = true
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText("Open").performClick()
|
||||
|
||||
assertThat(clicked).isTrue()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user