mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-15 07:28:30 +00:00
Migrate DataAndStorageFragment to compose.
This commit is contained in:
@@ -1,134 +1,269 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.data
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.Navigation
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.signal.core.ui.compose.Dividers
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.signal.core.ui.compose.Rows
|
||||
import org.signal.core.ui.compose.Scaffolds
|
||||
import org.signal.core.ui.compose.SignalPreview
|
||||
import org.signal.core.ui.compose.Texts
|
||||
import org.signal.core.util.bytes
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
import org.thoughtcrime.securesms.compose.rememberStatusBarColorNestedScrollModifier
|
||||
import org.thoughtcrime.securesms.mms.SentMediaQuality
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
import org.thoughtcrime.securesms.webrtc.CallDataMode
|
||||
import kotlin.math.abs
|
||||
|
||||
class DataAndStorageSettingsFragment : DSLSettingsFragment(R.string.preferences__data_and_storage) {
|
||||
class DataAndStorageSettingsFragment : ComposeFragment() {
|
||||
|
||||
private val autoDownloadValues by lazy { resources.getStringArray(R.array.pref_media_download_entries) }
|
||||
private val autoDownloadLabels by lazy { resources.getStringArray(R.array.pref_media_download_values) }
|
||||
|
||||
private val sentMediaQualityLabels by lazy { SentMediaQuality.getLabels(requireContext()) }
|
||||
|
||||
private val callDataModeLabels by lazy { resources.getStringArray(R.array.pref_data_and_storage_call_data_mode_values) }
|
||||
|
||||
private lateinit var viewModel: DataAndStorageSettingsViewModel
|
||||
private val viewModel: DataAndStorageSettingsViewModel by viewModels(
|
||||
factoryProducer = {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
val repository = DataAndStorageSettingsRepository()
|
||||
DataAndStorageSettingsViewModel.Factory(preferences, repository)
|
||||
}
|
||||
)
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
override fun bindAdapter(adapter: MappingAdapter) {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
val repository = DataAndStorageSettingsRepository()
|
||||
val factory = DataAndStorageSettingsViewModel.Factory(preferences, repository)
|
||||
viewModel = ViewModelProvider(this, factory)[DataAndStorageSettingsViewModel::class.java]
|
||||
@Composable
|
||||
override fun FragmentContent() {
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
val callbacks = remember { Callbacks() }
|
||||
|
||||
viewModel.state.observe(viewLifecycleOwner) {
|
||||
adapter.submitList(getConfiguration(it).toMappingModelList())
|
||||
}
|
||||
DataAndStorageSettingsScreen(
|
||||
state = state,
|
||||
callbacks = callbacks
|
||||
)
|
||||
}
|
||||
|
||||
fun getConfiguration(state: DataAndStorageSettingsState): DSLConfiguration {
|
||||
return configure {
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences_data_and_storage__manage_storage),
|
||||
summary = DSLSettingsText.from(state.totalStorageUse.bytes.toUnitString()),
|
||||
onClick = {
|
||||
Navigation.findNavController(requireView()).safeNavigate(R.id.action_dataAndStorageSettingsFragment_to_storagePreferenceFragment)
|
||||
}
|
||||
)
|
||||
private inner class Callbacks : DataAndStorageSettingsCallbacks {
|
||||
override fun onNavigationClick() {
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
dividerPref()
|
||||
override fun onManageStorageClick() {
|
||||
findNavController().safeNavigate(R.id.action_dataAndStorageSettingsFragment_to_storagePreferenceFragment)
|
||||
}
|
||||
|
||||
sectionHeaderPref(R.string.preferences_chats__media_auto_download)
|
||||
override fun onSentMediaQualitySelected(code: String) {
|
||||
viewModel.setSentMediaQuality(SentMediaQuality.fromCode(code.toInt()))
|
||||
}
|
||||
|
||||
multiSelectPref(
|
||||
title = DSLSettingsText.from(R.string.preferences_chats__when_using_mobile_data),
|
||||
listItems = autoDownloadLabels,
|
||||
selected = autoDownloadValues.map { state.mobileAutoDownloadValues.contains(it) }.toBooleanArray(),
|
||||
onSelected = {
|
||||
val resultSet = it.mapIndexed { index, selected -> if (selected) autoDownloadValues[index] else null }.filterNotNull().toSet()
|
||||
viewModel.setMobileAutoDownloadValues(resultSet)
|
||||
}
|
||||
)
|
||||
override fun onCallDataModeSelected(code: String) {
|
||||
viewModel.setCallDataMode(CallDataMode.fromCode(abs(code.toInt() - 2)))
|
||||
}
|
||||
|
||||
multiSelectPref(
|
||||
title = DSLSettingsText.from(R.string.preferences_chats__when_using_wifi),
|
||||
listItems = autoDownloadLabels,
|
||||
selected = autoDownloadValues.map { state.wifiAutoDownloadValues.contains(it) }.toBooleanArray(),
|
||||
onSelected = {
|
||||
val resultSet = it.mapIndexed { index, selected -> if (selected) autoDownloadValues[index] else null }.filterNotNull().toSet()
|
||||
viewModel.setWifiAutoDownloadValues(resultSet)
|
||||
}
|
||||
)
|
||||
override fun onUseProxyClick() {
|
||||
findNavController().safeNavigate(R.id.action_dataAndStorageSettingsFragment_to_editProxyFragment)
|
||||
}
|
||||
|
||||
multiSelectPref(
|
||||
title = DSLSettingsText.from(R.string.preferences_chats__when_roaming),
|
||||
listItems = autoDownloadLabels,
|
||||
selected = autoDownloadValues.map { state.roamingAutoDownloadValues.contains(it) }.toBooleanArray(),
|
||||
onSelected = {
|
||||
val resultSet = it.mapIndexed { index, selected -> if (selected) autoDownloadValues[index] else null }.filterNotNull().toSet()
|
||||
viewModel.setRoamingAutoDownloadValues(resultSet)
|
||||
}
|
||||
)
|
||||
override fun onMobileDataAutoDownloadSelectionChanged(selection: Array<String>) {
|
||||
viewModel.setMobileAutoDownloadValues(selection.toSet())
|
||||
}
|
||||
|
||||
dividerPref()
|
||||
override fun onWifiDataAutoDownloadSelectionChanged(selection: Array<String>) {
|
||||
viewModel.setWifiAutoDownloadValues(selection.toSet())
|
||||
}
|
||||
|
||||
sectionHeaderPref(R.string.DataAndStorageSettingsFragment__media_quality)
|
||||
|
||||
radioListPref(
|
||||
title = DSLSettingsText.from(R.string.DataAndStorageSettingsFragment__sent_media_quality),
|
||||
listItems = sentMediaQualityLabels,
|
||||
selected = SentMediaQuality.entries.indexOf(state.sentMediaQuality),
|
||||
onSelected = { viewModel.setSentMediaQuality(SentMediaQuality.entries[it]) }
|
||||
)
|
||||
|
||||
textPref(
|
||||
summary = DSLSettingsText.from(R.string.DataAndStorageSettingsFragment__sending_high_quality_media_will_use_more_data)
|
||||
)
|
||||
|
||||
dividerPref()
|
||||
|
||||
sectionHeaderPref(R.string.DataAndStorageSettingsFragment__calls)
|
||||
|
||||
radioListPref(
|
||||
title = DSLSettingsText.from(R.string.preferences_data_and_storage__use_less_data_for_calls),
|
||||
listItems = callDataModeLabels,
|
||||
selected = abs(state.callDataMode.code - 2),
|
||||
onSelected = {
|
||||
viewModel.setCallDataMode(CallDataMode.fromCode(abs(it - 2)))
|
||||
}
|
||||
)
|
||||
|
||||
textPref(
|
||||
summary = DSLSettingsText.from(R.string.preference_data_and_storage__using_less_data_may_improve_calls_on_bad_networks)
|
||||
)
|
||||
|
||||
dividerPref()
|
||||
|
||||
sectionHeaderPref(R.string.preferences_proxy)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences_use_proxy),
|
||||
summary = DSLSettingsText.from(if (state.isProxyEnabled) R.string.preferences_on else R.string.preferences_off),
|
||||
onClick = {
|
||||
Navigation.findNavController(requireView()).safeNavigate(R.id.action_dataAndStorageSettingsFragment_to_editProxyFragment)
|
||||
}
|
||||
)
|
||||
override fun onRoamingDataAutoDownloadSelectionChanged(selection: Array<String>) {
|
||||
viewModel.setRoamingAutoDownloadValues(selection.toSet())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface DataAndStorageSettingsCallbacks {
|
||||
fun onNavigationClick() = Unit
|
||||
fun onManageStorageClick() = Unit
|
||||
fun onSentMediaQualitySelected(code: String) = Unit
|
||||
fun onCallDataModeSelected(code: String) = Unit
|
||||
fun onUseProxyClick() = Unit
|
||||
fun onMobileDataAutoDownloadSelectionChanged(selection: Array<String>) = Unit
|
||||
fun onWifiDataAutoDownloadSelectionChanged(selection: Array<String>) = Unit
|
||||
fun onRoamingDataAutoDownloadSelectionChanged(selection: Array<String>) = Unit
|
||||
|
||||
object Empty : DataAndStorageSettingsCallbacks
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DataAndStorageSettingsScreen(
|
||||
state: DataAndStorageSettingsState,
|
||||
callbacks: DataAndStorageSettingsCallbacks
|
||||
) {
|
||||
Scaffolds.Settings(
|
||||
title = stringResource(R.string.preferences__data_and_storage),
|
||||
onNavigationClick = callbacks::onNavigationClick,
|
||||
navigationIcon = ImageVector.vectorResource(R.drawable.symbol_arrow_start_24)
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.then(rememberStatusBarColorNestedScrollModifier())
|
||||
) {
|
||||
item {
|
||||
Rows.TextRow(
|
||||
text = stringResource(R.string.preferences_data_and_storage__manage_storage),
|
||||
label = state.totalStorageUse.bytes.toUnitString(),
|
||||
onClick = callbacks::onManageStorageClick
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Dividers.Default()
|
||||
}
|
||||
|
||||
item {
|
||||
Texts.SectionHeader(stringResource(R.string.preferences_chats__media_auto_download))
|
||||
}
|
||||
|
||||
item {
|
||||
Rows.MultiSelectRow(
|
||||
text = stringResource(R.string.preferences_chats__when_using_mobile_data),
|
||||
labels = stringArrayResource(R.array.pref_media_download_entries),
|
||||
values = stringArrayResource(R.array.pref_media_download_values),
|
||||
selection = state.mobileAutoDownloadValues.toTypedArray(),
|
||||
onSelectionChanged = callbacks::onMobileDataAutoDownloadSelectionChanged
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Rows.MultiSelectRow(
|
||||
text = stringResource(R.string.preferences_chats__when_using_wifi),
|
||||
labels = stringArrayResource(R.array.pref_media_download_entries),
|
||||
values = stringArrayResource(R.array.pref_media_download_values),
|
||||
selection = state.wifiAutoDownloadValues.toTypedArray(),
|
||||
onSelectionChanged = callbacks::onWifiDataAutoDownloadSelectionChanged
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Rows.MultiSelectRow(
|
||||
text = stringResource(R.string.preferences_chats__when_roaming),
|
||||
labels = stringArrayResource(R.array.pref_media_download_entries),
|
||||
values = stringArrayResource(R.array.pref_media_download_values),
|
||||
selection = state.roamingAutoDownloadValues.toTypedArray(),
|
||||
onSelectionChanged = callbacks::onRoamingDataAutoDownloadSelectionChanged
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Dividers.Default()
|
||||
}
|
||||
|
||||
item {
|
||||
Texts.SectionHeader(stringResource(R.string.DataAndStorageSettingsFragment__media_quality))
|
||||
}
|
||||
|
||||
item {
|
||||
val context = LocalContext.current
|
||||
val labels = remember { SentMediaQuality.getLabels(context) }
|
||||
|
||||
Rows.RadioListRow(
|
||||
text = stringResource(R.string.DataAndStorageSettingsFragment__sent_media_quality),
|
||||
labels = labels,
|
||||
values = SentMediaQuality.entries.map { it.code.toString() }.toTypedArray(),
|
||||
selectedValue = state.sentMediaQuality.code.toString(),
|
||||
onSelected = callbacks::onSentMediaQualitySelected
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Rows.TextRow(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.DataAndStorageSettingsFragment__sending_high_quality_media_will_use_more_data),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Dividers.Default()
|
||||
}
|
||||
|
||||
item {
|
||||
Texts.SectionHeader(stringResource(R.string.DataAndStorageSettingsFragment__calls))
|
||||
}
|
||||
|
||||
item {
|
||||
Rows.RadioListRow(
|
||||
text = stringResource(R.string.preferences_data_and_storage__use_less_data_for_calls),
|
||||
labels = stringArrayResource(R.array.pref_data_and_storage_call_data_mode_values),
|
||||
values = CallDataMode.entries.map { it.code.toString() }.toTypedArray(),
|
||||
selectedValue = abs(state.callDataMode.code - 2).toString(),
|
||||
onSelected = callbacks::onCallDataModeSelected
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Rows.TextRow(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.preference_data_and_storage__using_less_data_may_improve_calls_on_bad_networks),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Dividers.Default()
|
||||
}
|
||||
|
||||
item {
|
||||
Texts.SectionHeader(stringResource(R.string.preferences_proxy))
|
||||
}
|
||||
|
||||
item {
|
||||
Rows.TextRow(
|
||||
text = stringResource(R.string.preferences_use_proxy),
|
||||
label = stringResource(if (state.isProxyEnabled) R.string.preferences_on else R.string.preferences_off),
|
||||
onClick = callbacks::onUseProxyClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun DataAndStorageSettingsScreenPreview() {
|
||||
Previews.Preview {
|
||||
DataAndStorageSettingsScreen(
|
||||
state = DataAndStorageSettingsState(
|
||||
totalStorageUse = 100_000,
|
||||
mobileAutoDownloadValues = setOf(),
|
||||
wifiAutoDownloadValues = setOf(),
|
||||
roamingAutoDownloadValues = setOf(),
|
||||
callDataMode = CallDataMode.HIGH_ALWAYS,
|
||||
isProxyEnabled = false,
|
||||
sentMediaQuality = SentMediaQuality.STANDARD
|
||||
),
|
||||
callbacks = DataAndStorageSettingsCallbacks.Empty
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.data
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.mms.SentMediaQuality
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
import org.thoughtcrime.securesms.webrtc.CallDataMode
|
||||
|
||||
class DataAndStorageSettingsViewModel(
|
||||
@@ -16,9 +17,9 @@ class DataAndStorageSettingsViewModel(
|
||||
private val repository: DataAndStorageSettingsRepository
|
||||
) : ViewModel() {
|
||||
|
||||
private val store = Store(getState())
|
||||
private val store = MutableStateFlow(getState())
|
||||
|
||||
val state: LiveData<DataAndStorageSettingsState> = store.stateLiveData
|
||||
val state: StateFlow<DataAndStorageSettingsState> = store
|
||||
|
||||
fun refresh() {
|
||||
repository.getTotalStorageUse { totalStorageUse ->
|
||||
|
||||
@@ -251,14 +251,14 @@
|
||||
</string-array>
|
||||
|
||||
<!-- discrete MIME type (the part before the "/") -->
|
||||
<string-array name="pref_media_download_entries">
|
||||
<string-array name="pref_media_download_values">
|
||||
<item>image</item>
|
||||
<item>audio</item>
|
||||
<item>video</item>
|
||||
<item>documents</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref_media_download_values">
|
||||
<string-array name="pref_media_download_entries">
|
||||
<item>@string/arrays__images</item>
|
||||
<item>@string/arrays__audio</item>
|
||||
<item>@string/arrays__video</item>
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -33,6 +34,10 @@ import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -41,6 +46,7 @@ import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -377,7 +383,9 @@ object Dialogs {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier = Modifier.padding(top = 16.dp).horizontalGutters()
|
||||
modifier = Modifier
|
||||
.padding(top = 16.dp)
|
||||
.horizontalGutters()
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
@@ -420,6 +428,106 @@ object Dialogs {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MultiSelectListDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
properties: DialogProperties = DialogProperties(),
|
||||
title: String,
|
||||
labels: Array<String>,
|
||||
values: Array<String>,
|
||||
selection: Array<String>,
|
||||
onSelectionChanged: (Array<String>) -> Unit
|
||||
) {
|
||||
var selectedIndicies by remember {
|
||||
mutableStateOf(
|
||||
values.mapIndexedNotNull { index, value ->
|
||||
if (value in selection) {
|
||||
index
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
properties = properties
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 100.dp)
|
||||
.background(
|
||||
color = SignalTheme.colors.colorSurface2,
|
||||
shape = AlertDialogDefaults.shape
|
||||
)
|
||||
.clip(AlertDialogDefaults.shape)
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier = Modifier
|
||||
.padding(top = 16.dp)
|
||||
.horizontalGutters()
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(top = 24.dp, bottom = 16.dp)
|
||||
) {
|
||||
items(
|
||||
count = values.size,
|
||||
key = { values[it] }
|
||||
) { index ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.defaultMinSize(minHeight = 48.dp)
|
||||
.clickable(
|
||||
enabled = true,
|
||||
onClick = {
|
||||
selectedIndicies = if (index in selectedIndicies) {
|
||||
selectedIndicies - index
|
||||
} else {
|
||||
selectedIndicies + index
|
||||
}
|
||||
}
|
||||
)
|
||||
.horizontalGutters()
|
||||
) {
|
||||
Checkbox(
|
||||
enabled = true,
|
||||
checked = index in selectedIndicies,
|
||||
onCheckedChange = null,
|
||||
modifier = Modifier.padding(end = 24.dp)
|
||||
)
|
||||
|
||||
Text(text = labels[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.End,
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp)
|
||||
) {
|
||||
TextButton(onClick = onDismissRequest) {
|
||||
Text(text = stringResource(R.string.cancel))
|
||||
}
|
||||
|
||||
TextButton(onClick = {
|
||||
onSelectionChanged(selectedIndicies.sorted().map { values[it] }.toTypedArray())
|
||||
onDismissRequest()
|
||||
}) {
|
||||
Text(text = stringResource(R.string.ok))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alert dialog that supports three options.
|
||||
* If you only need two options (confirm/dismiss), use [SimpleAlertDialog] instead.
|
||||
|
||||
@@ -183,6 +183,52 @@ object Rows {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
multiSelectPref(
|
||||
text = stringResource(R.string.preferences_chats__when_using_mobile_data),
|
||||
listItems = autoDownloadLabels,
|
||||
selected = autoDownloadValues.map { state.mobileAutoDownloadValues.contains(it) }.toBooleanArray(),
|
||||
onSelected = {
|
||||
val resultSet = it.mapIndexed { index, selected -> if (selected) autoDownloadValues[index] else null }.filterNotNull().toSet()
|
||||
viewModel.setMobileAutoDownloadValues(resultSet)
|
||||
}
|
||||
)
|
||||
*/
|
||||
|
||||
@Composable
|
||||
fun MultiSelectRow(
|
||||
text: String,
|
||||
labels: Array<String>,
|
||||
values: Array<String>,
|
||||
selection: Array<String>,
|
||||
onSelectionChanged: (Array<String>) -> Unit
|
||||
) {
|
||||
var displayDialog by remember { mutableStateOf(false) }
|
||||
|
||||
TextRow(
|
||||
text = text,
|
||||
label = selection.joinToString(", ") {
|
||||
val index = values.indexOf(it)
|
||||
if (index == -1) error("not found: $it in ${values.joinToString(", ")}")
|
||||
labels[index]
|
||||
},
|
||||
onClick = {
|
||||
displayDialog = true
|
||||
}
|
||||
)
|
||||
|
||||
if (displayDialog) {
|
||||
Dialogs.MultiSelectListDialog(
|
||||
onDismissRequest = { displayDialog = false },
|
||||
labels = labels,
|
||||
values = values,
|
||||
selection = selection,
|
||||
title = text,
|
||||
onSelectionChanged = onSelectionChanged
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Row that positions [text] and optional [label] in a [TextAndLabel] to the side of a [Switch].
|
||||
*
|
||||
@@ -622,3 +668,21 @@ private fun RadioListRowPreview() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun MultiSelectRowPreview() {
|
||||
var selectedValues by remember { mutableStateOf(arrayOf("b")) }
|
||||
|
||||
Previews.Preview {
|
||||
Rows.MultiSelectRow(
|
||||
text = "MultiSelect List",
|
||||
labels = arrayOf("A", "B", "C"),
|
||||
values = arrayOf("a", "b", "c"),
|
||||
selection = selectedValues,
|
||||
onSelectionChanged = {
|
||||
selectedValues = it
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user