Merge "Create TwoTargetSwitchPreference for SpaLib"
This commit is contained in:
@@ -25,6 +25,7 @@ import com.android.settingslib.spa.gallery.page.SliderPageProvider
|
||||
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
|
||||
import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
|
||||
import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
|
||||
import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
|
||||
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
|
||||
|
||||
val galleryPageProviders = SettingsPageProviderRepository(
|
||||
@@ -33,6 +34,7 @@ val galleryPageProviders = SettingsPageProviderRepository(
|
||||
PreferenceMainPageProvider,
|
||||
PreferencePageProvider,
|
||||
SwitchPreferencePageProvider,
|
||||
TwoTargetSwitchPreferencePageProvider,
|
||||
ArgumentPageProvider,
|
||||
SliderPageProvider,
|
||||
SpinnerPageProvider,
|
||||
|
||||
@@ -48,5 +48,6 @@ private fun PreferenceMain() {
|
||||
RegularScaffold(title = TITLE) {
|
||||
PreferencePageProvider.EntryItem()
|
||||
SwitchPreferencePageProvider.EntryItem()
|
||||
TwoTargetSwitchPreferencePageProvider.EntryItem()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.preference
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
||||
import com.android.settingslib.spa.framework.compose.navigator
|
||||
import com.android.settingslib.spa.framework.compose.stateOf
|
||||
import com.android.settingslib.spa.framework.theme.SettingsTheme
|
||||
import com.android.settingslib.spa.widget.preference.Preference
|
||||
import com.android.settingslib.spa.widget.preference.PreferenceModel
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||
import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
|
||||
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
private const val TITLE = "Sample TwoTargetSwitchPreference"
|
||||
|
||||
object TwoTargetSwitchPreferencePageProvider : SettingsPageProvider {
|
||||
override val name = "TwoTargetSwitchPreference"
|
||||
|
||||
@Composable
|
||||
override fun Page(arguments: Bundle?) {
|
||||
TwoTargetSwitchPreferencePage()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EntryItem() {
|
||||
Preference(object : PreferenceModel {
|
||||
override val title = TITLE
|
||||
override val onClick = navigator(name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TwoTargetSwitchPreferencePage() {
|
||||
RegularScaffold(title = TITLE) {
|
||||
SampleTwoTargetSwitchPreference()
|
||||
SampleTwoTargetSwitchPreferenceWithSummary()
|
||||
SampleTwoTargetSwitchPreferenceWithAsyncSummary()
|
||||
SampleNotChangeableTwoTargetSwitchPreference()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SampleTwoTargetSwitchPreference() {
|
||||
val checked = rememberSaveable { mutableStateOf(false) }
|
||||
TwoTargetSwitchPreference(remember {
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = "TwoTargetSwitchPreference"
|
||||
override val checked = checked
|
||||
override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
|
||||
}
|
||||
}) {}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SampleTwoTargetSwitchPreferenceWithSummary() {
|
||||
val checked = rememberSaveable { mutableStateOf(true) }
|
||||
TwoTargetSwitchPreference(remember {
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = "TwoTargetSwitchPreference"
|
||||
override val summary = stateOf("With summary")
|
||||
override val checked = checked
|
||||
override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
|
||||
}
|
||||
}) {}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SampleTwoTargetSwitchPreferenceWithAsyncSummary() {
|
||||
val checked = rememberSaveable { mutableStateOf(true) }
|
||||
val summary = produceState(initialValue = " ") {
|
||||
delay(1000L)
|
||||
value = "Async summary"
|
||||
}
|
||||
TwoTargetSwitchPreference(remember {
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = "TwoTargetSwitchPreference"
|
||||
override val summary = summary
|
||||
override val checked = checked
|
||||
override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
|
||||
}
|
||||
}) {}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SampleNotChangeableTwoTargetSwitchPreference() {
|
||||
val checked = rememberSaveable { mutableStateOf(true) }
|
||||
TwoTargetSwitchPreference(remember {
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = "TwoTargetSwitchPreference"
|
||||
override val summary = stateOf("Not changeable")
|
||||
override val changeable = stateOf(false)
|
||||
override val checked = checked
|
||||
override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
|
||||
}
|
||||
}) {}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun TwoTargetSwitchPreferencePagePreview() {
|
||||
SettingsTheme {
|
||||
TwoTargetSwitchPreferencePage()
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ object SettingsDimension {
|
||||
bottom = itemPaddingVertical,
|
||||
)
|
||||
val itemPaddingAround = 8.dp
|
||||
val itemDividerHeight = 32.dp
|
||||
|
||||
/** The size when app icon is displayed in list. */
|
||||
val appIconItemSize = 32.dp
|
||||
|
||||
@@ -19,4 +19,5 @@ package com.android.settingslib.spa.framework.theme
|
||||
object SettingsOpacity {
|
||||
const val Full = 1f
|
||||
const val Disabled = 0.38f
|
||||
const val Divider = 0.2f
|
||||
}
|
||||
|
||||
@@ -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 com.android.settingslib.spa.widget.preference
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.remember
|
||||
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
|
||||
|
||||
@Composable
|
||||
internal fun TwoTargetPreference(
|
||||
title: String,
|
||||
summary: State<String>,
|
||||
onClick: () -> Unit,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
widget: @Composable () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = SettingsDimension.itemPaddingEnd),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
Preference(remember {
|
||||
object : PreferenceModel {
|
||||
override val title = title
|
||||
override val summary = summary
|
||||
override val icon = icon
|
||||
override val onClick = onClick
|
||||
}
|
||||
})
|
||||
}
|
||||
PreferenceDivider()
|
||||
widget()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PreferenceDivider() {
|
||||
Box(
|
||||
Modifier
|
||||
.padding(horizontal = SettingsDimension.itemPaddingEnd)
|
||||
.size(width = 1.dp, height = SettingsDimension.itemDividerHeight)
|
||||
.background(color = MaterialTheme.colorScheme.onSurface.copy(SettingsOpacity.Disabled))
|
||||
)
|
||||
}
|
||||
@@ -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 com.android.settingslib.spa.widget.preference
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.android.settingslib.spa.widget.ui.SettingsSwitch
|
||||
|
||||
@Composable
|
||||
fun TwoTargetSwitchPreference(
|
||||
model: SwitchPreferenceModel,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TwoTargetPreference(
|
||||
title = model.title,
|
||||
summary = model.summary,
|
||||
onClick = onClick,
|
||||
icon = icon,
|
||||
) {
|
||||
SettingsSwitch(
|
||||
checked = model.checked,
|
||||
changeable = model.changeable,
|
||||
onCheckedChange = model.onCheckedChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.framework.theme.SettingsTheme
|
||||
@@ -47,6 +48,8 @@ fun SettingsScaffold(
|
||||
Text(
|
||||
text = title,
|
||||
modifier = Modifier.padding(SettingsDimension.itemPaddingAround),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
},
|
||||
navigationIcon = { NavigateBack() },
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.preference
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertIsOff
|
||||
import androidx.compose.ui.test.assertIsOn
|
||||
import androidx.compose.ui.test.isToggleable
|
||||
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.android.settingslib.spa.framework.compose.stateOf
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class TwoTargetSwitchPreferenceTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun title_displayed() {
|
||||
val checked = mutableStateOf(false)
|
||||
composeTestRule.setContent {
|
||||
TestTwoTargetSwitchPreference(checked = checked, changeable = true)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText("TwoTargetSwitchPreference").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toggleable_initialStateIsCorrect() {
|
||||
val checked = mutableStateOf(false)
|
||||
composeTestRule.setContent {
|
||||
TestTwoTargetSwitchPreference(checked = checked, changeable = true)
|
||||
}
|
||||
|
||||
composeTestRule.onNode(isToggleable()).assertIsOff()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toggleable_changeable_withEffect() {
|
||||
val checked = mutableStateOf(false)
|
||||
composeTestRule.setContent {
|
||||
TestTwoTargetSwitchPreference(checked = checked, changeable = true)
|
||||
}
|
||||
|
||||
composeTestRule.onNode(isToggleable()).performClick()
|
||||
composeTestRule.onNode(isToggleable()).assertIsOn()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toggleable_notChangeable_noEffect() {
|
||||
val checked = mutableStateOf(false)
|
||||
composeTestRule.setContent {
|
||||
TestTwoTargetSwitchPreference(checked = checked, changeable = false)
|
||||
}
|
||||
|
||||
composeTestRule.onNode(isToggleable()).performClick()
|
||||
composeTestRule.onNode(isToggleable()).assertIsOff()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun clickable_canBeClick() {
|
||||
val checked = mutableStateOf(false)
|
||||
var clicked = false
|
||||
composeTestRule.setContent {
|
||||
TestTwoTargetSwitchPreference(checked = checked, changeable = false) {
|
||||
clicked = true
|
||||
}
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText("TwoTargetSwitchPreference").performClick()
|
||||
assertThat(clicked).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TestTwoTargetSwitchPreference(
|
||||
checked: MutableState<Boolean>,
|
||||
changeable: Boolean,
|
||||
onClick: () -> Unit = {},
|
||||
) {
|
||||
TwoTargetSwitchPreference(
|
||||
model = remember {
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = "TwoTargetSwitchPreference"
|
||||
override val checked = checked
|
||||
override val changeable = stateOf(changeable)
|
||||
override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
|
||||
}
|
||||
},
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user