mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01: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 ->
|
||||
|
||||
Reference in New Issue
Block a user