From 78eef350b5056ff12ef44f13c4b1215c303c4a65 Mon Sep 17 00:00:00 2001 From: andrew Date: Mon, 21 Aug 2023 19:45:50 +0930 Subject: [PATCH] Add radioOption DSL --- .../expiration/ExpirationSettingsActivity.kt | 148 +++++++----------- .../expiration/ExpirationSettingsViewModel.kt | 2 + .../preferences/ClearAllDataDialog.kt | 4 +- .../preferences/RadioOptionAdapter.kt | 77 +++++++-- .../org/thoughtcrime/securesms/ui/Data.kt | 5 + 5 files changed, 130 insertions(+), 106 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsActivity.kt index 5c8b8c54de..3bc8b8ef43 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsActivity.kt @@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.preferences.ExpirationRadioOption import org.thoughtcrime.securesms.preferences.RadioOptionAdapter +import org.thoughtcrime.securesms.preferences.radioOption import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import javax.inject.Inject import kotlin.math.max @@ -43,30 +44,27 @@ class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() { } private val viewModel: ExpirationSettingsViewModel by viewModels { - val afterReadOptions = resources.getIntArray(R.array.read_expiration_time_values).map(Int::toString) + val afterReadOptions = resources.getIntArray(R.array.read_expiration_time_values) .zip(resources.getStringArray(R.array.read_expiration_time_names)) { value, name -> - ExpirationRadioOption(ExpiryMode.AfterRead(value.toLong()), name, getString(R.string.AccessibilityId_time_option)) + radioOption(ExpiryMode.AfterRead(value.toLong()), name) { contentDescription(R.string.AccessibilityId_time_option) } } - val afterSendOptions = resources.getIntArray(R.array.send_expiration_time_values).map(Int::toString) + val afterSendOptions = resources.getIntArray(R.array.send_expiration_time_values) .zip(resources.getStringArray(R.array.send_expiration_time_names)) { value, name -> - ExpirationRadioOption(ExpiryMode.AfterSend(value.toLong()), name, getString(R.string.AccessibilityId_time_option)) + radioOption(ExpiryMode.AfterSend(value.toLong()), name) { contentDescription(R.string.AccessibilityId_time_option) } } viewModelFactory.create(threadId, mayAddTestExpiryOption(afterReadOptions), mayAddTestExpiryOption(afterSendOptions)) } - private fun mayAddTestExpiryOption(expiryOptions: List): List { - return if (BuildConfig.DEBUG) { - val options = expiryOptions.toMutableList() - val added = when (options.first().value) { + private fun mayAddTestExpiryOption(expiryOptions: List): List = + if (BuildConfig.DEBUG) { + when (expiryOptions.first().value) { is ExpiryMode.AfterRead -> ExpiryMode.AfterRead(60) is ExpiryMode.AfterSend -> ExpiryMode.AfterSend(60) is ExpiryMode.Legacy -> ExpiryMode.Legacy(60) ExpiryMode.NONE -> ExpiryMode.NONE // shouldn't happen - } - options.add(1, ExpirationRadioOption(added, "1 Minute (for testing purposes)")) - options + }.let { radioOption(it, "1 Minute (for testing purposes)") } + .let { expiryOptions.toMutableList().apply { add(1, it) } } } else expiryOptions - } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) @@ -184,55 +182,41 @@ class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() { if (viewModel.recipient.value?.isContactRecipient == true && viewModel.recipient.value?.isLocalNumber == false) { deleteTypeOptions.addAll( listOf( - ExpirationRadioOption( - value = ExpiryMode.NONE, - title = getString(R.string.expiration_off), - contentDescription = getString(R.string.AccessibilityId_disable_disappearing_messages) - ), - ExpirationRadioOption( - value = ExpiryMode.AfterRead(0), - title = getString(R.string.expiration_type_disappear_after_read), - subtitle = getString(R.string.expiration_type_disappear_after_read_description), - contentDescription = getString(R.string.AccessibilityId_disappear_after_read_option) - ), - ExpirationRadioOption( - value = ExpiryMode.AfterSend(0), - title = getString(R.string.expiration_type_disappear_after_send), - subtitle = getString(R.string.expiration_type_disappear_after_send_description), - contentDescription = getString(R.string.AccessibilityId_disappear_after_send_option) - ) + radioOption(ExpiryMode.NONE, R.string.expiration_off) { + contentDescription(R.string.AccessibilityId_disable_disappearing_messages) + }, + radioOption(ExpiryMode.AfterRead(0), R.string.expiration_type_disappear_after_read) { + subtitle(R.string.expiration_type_disappear_after_read_description) + contentDescription(R.string.AccessibilityId_disappear_after_read_option) + }, + radioOption(ExpiryMode.AfterSend(0), R.string.expiration_type_disappear_after_send) { + subtitle(R.string.expiration_type_disappear_after_send_description) + contentDescription(R.string.AccessibilityId_disappear_after_send_option) + } ) ) } else if (viewModel.recipient.value?.isLocalNumber == true) { deleteTypeOptions.addAll( listOf( - ExpirationRadioOption( - value = ExpiryMode.NONE, - title = getString(R.string.expiration_off), - contentDescription = getString(R.string.AccessibilityId_disable_disappearing_messages) - ), - ExpirationRadioOption( - value = ExpiryMode.AfterSend(0), - title = getString(R.string.expiration_type_disappear_after_send), - subtitle = getString(R.string.expiration_type_disappear_after_send_description), - contentDescription = getString(R.string.AccessibilityId_disappear_after_send_option) - ) + radioOption(ExpiryMode.NONE, R.string.expiration_off) { + contentDescription(R.string.AccessibilityId_disable_disappearing_messages) + }, + radioOption(ExpiryMode.AfterSend(0), R.string.expiration_type_disappear_after_send) { + subtitle(R.string.expiration_type_disappear_after_send_description) + contentDescription(R.string.AccessibilityId_disappear_after_send_option) + } ) ) } else if (viewModel.recipient.value?.isClosedGroupRecipient == true) { deleteTypeOptions.addAll( listOf( - ExpirationRadioOption( - value = ExpiryMode.NONE, - title = getString(R.string.expiration_off), - contentDescription = getString(R.string.AccessibilityId_disable_disappearing_messages) - ), - ExpirationRadioOption( - value = ExpiryMode.AfterSend(0), - title = getString(R.string.expiration_type_disappear_after_send), - subtitle = getString(R.string.expiration_type_disappear_after_send_description), - contentDescription = getString(R.string.AccessibilityId_disappear_after_send_option) - ) + radioOption(ExpiryMode.NONE, R.string.expiration_off) { + contentDescription(R.string.AccessibilityId_disable_disappearing_messages) + }, + radioOption(ExpiryMode.AfterSend(0), R.string.expiration_type_disappear_after_send) { + subtitle(R.string.expiration_type_disappear_after_send_description) + contentDescription(R.string.AccessibilityId_disappear_after_send_option) + } ) ) } @@ -240,48 +224,36 @@ class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() { if (viewModel.recipient.value?.isContactRecipient == true && viewModel.recipient.value?.isLocalNumber == false) { deleteTypeOptions.addAll( listOf( - ExpirationRadioOption( - value = ExpiryMode.NONE, - title = getString(R.string.expiration_off), - contentDescription = getString(R.string.AccessibilityId_disable_disappearing_messages) - ), - ExpirationRadioOption( - value = ExpiryMode.Legacy(0), - title = getString(R.string.expiration_type_disappear_legacy), - subtitle = getString(R.string.expiration_type_disappear_legacy_description) - ), - ExpirationRadioOption( - value = ExpiryMode.AfterRead(0), - title = getString(R.string.expiration_type_disappear_after_read), - subtitle = getString(R.string.expiration_type_disappear_after_read_description), - enabled = false, - contentDescription = getString(R.string.AccessibilityId_disappear_after_read_option) - ), - ExpirationRadioOption( - value = ExpiryMode.AfterSend(0), - title = getString(R.string.expiration_type_disappear_after_send), - subtitle = getString(R.string.expiration_type_disappear_after_send_description), - enabled = false, - contentDescription = getString(R.string.AccessibilityId_disappear_after_send_option) - ) + radioOption(ExpiryMode.NONE, R.string.expiration_off) { + contentDescription(R.string.AccessibilityId_disable_disappearing_messages) + }, + radioOption(ExpiryMode.Legacy(0), R.string.expiration_type_disappear_legacy) { + subtitle(R.string.expiration_type_disappear_legacy_description) + }, + radioOption(ExpiryMode.AfterRead(0), R.string.expiration_type_disappear_after_read) { + subtitle(R.string.expiration_type_disappear_after_read_description) + enabled = false + contentDescription(R.string.AccessibilityId_disappear_after_read_option) + }, + radioOption(ExpiryMode.AfterSend(0), R.string.expiration_type_disappear_after_send) { + subtitle(R.string.expiration_type_disappear_after_send_description) + enabled = false + contentDescription(R.string.AccessibilityId_disappear_after_send_option) + } ) ) } else { deleteTypeOptions.addAll( listOf( - ExpirationRadioOption(value = ExpiryMode.NONE, title = getString(R.string.expiration_off)), - ExpirationRadioOption( - value = ExpiryMode.Legacy(0), - title = getString(R.string.expiration_type_disappear_legacy), - subtitle = getString(R.string.expiration_type_disappear_legacy_description) - ), - ExpirationRadioOption( - value = ExpiryMode.AfterSend(0), - title = getString(R.string.expiration_type_disappear_after_send), - subtitle = getString(R.string.expiration_type_disappear_after_send_description), - enabled = false, - contentDescription = getString(R.string.AccessibilityId_disappear_after_send_option) - ) + radioOption(ExpiryMode.NONE, R.string.expiration_off), + radioOption(ExpiryMode.Legacy(0), R.string.expiration_type_disappear_legacy) { + subtitle(R.string.expiration_type_disappear_legacy_description) + }, + radioOption(ExpiryMode.AfterSend(0), R.string.expiration_type_disappear_after_send) { + subtitle(R.string.expiration_type_disappear_after_send_description) + enabled = false + contentDescription(R.string.AccessibilityId_disappear_after_send_option) + } ) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt index ec2ba8c1ea..394ad5316e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.launchIn @@ -27,6 +28,7 @@ import org.thoughtcrime.securesms.preferences.ExpirationRadioOption import org.thoughtcrime.securesms.preferences.RadioOption import kotlin.reflect.KClass +@OptIn(ExperimentalCoroutinesApi::class) class ExpirationSettingsViewModel( private val threadId: Long, private val afterReadOptions: List, diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt index f747d3eb91..31f2782f8f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt @@ -46,8 +46,8 @@ class ClearAllDataDialog : DialogFragment() { private fun createView(): View { binding = DialogClearAllDataBinding.inflate(LayoutInflater.from(requireContext())) - val device = StringRadioOption("deviceOnly", requireContext().getString(R.string.dialog_clear_all_data_clear_device_only)) - val network = StringRadioOption("deviceAndNetwork", requireContext().getString(R.string.dialog_clear_all_data_clear_device_and_network)) + val device = radioOption("deviceOnly", R.string.dialog_clear_all_data_clear_device_only) + val network = radioOption("deviceAndNetwork", R.string.dialog_clear_all_data_clear_device_and_network) var selectedOption: RadioOption = device val optionAdapter = RadioOptionAdapter { selectedOption = it } binding.recyclerView.apply { diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt index d4c4f77716..8f74d89a0b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/RadioOptionAdapter.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.preferences import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.StringRes import androidx.core.view.isVisible import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter @@ -11,6 +12,7 @@ import network.loki.messenger.R import network.loki.messenger.databinding.ItemSelectableBinding import network.loki.messenger.libsession_util.util.ExpiryMode import org.thoughtcrime.securesms.mms.GlideApp +import org.thoughtcrime.securesms.ui.GetString import java.util.Objects class RadioOptionAdapter( @@ -23,15 +25,15 @@ class RadioOptionAdapter( override fun areContentsTheSame(oldItem: RadioOption, newItem: RadioOption) = Objects.equals(oldItem.value,newItem.value) } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_selectable, parent, false) - return ViewHolder(itemView) - } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = + LayoutInflater.from(parent.context).inflate(R.layout.item_selectable, parent, false) + .let(::ViewHolder) override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val option = getItem(position) - val isSelected = position == selectedOptionPosition - holder.bind(option, isSelected) { + holder.bind( + option = getItem(position), + isSelected = position == selectedOptionPosition + ) { onClickListener(it) selectedOptionPosition = position notifyItemRangeChanged(0, itemCount) @@ -44,21 +46,22 @@ class RadioOptionAdapter( } class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { - val glide = GlideApp.with(itemView) val binding = ItemSelectableBinding.bind(itemView) fun bind(option: RadioOption, isSelected: Boolean, toggleSelection: (RadioOption) -> Unit) { val alpha = if (option.enabled) 1f else 0.5f binding.root.isEnabled = option.enabled - binding.root.contentDescription = option.contentDescription + binding.root.contentDescription = option.contentDescription?.string(itemView.context) binding.titleTextView.alpha = alpha binding.subtitleTextView.alpha = alpha binding.selectButton.alpha = alpha - binding.titleTextView.text = option.title - binding.subtitleTextView.text = option.subtitle - binding.subtitleTextView.isVisible = !option.subtitle.isNullOrEmpty() + binding.titleTextView.text = option.title.string(itemView.context) + binding.subtitleTextView.text = option.subtitle?.string(itemView.context).also { + binding.subtitleTextView.isVisible = !it.isNullOrBlank() + } + binding.selectButton.isSelected = isSelected if (option.enabled) { binding.root.setOnClickListener { toggleSelection(option) } @@ -68,13 +71,55 @@ class RadioOptionAdapter( } -data class RadioOption( +data class RadioOption( val value: T, - val title: String, - val subtitle: String? = null, + val title: GetString, + val subtitle: GetString? = null, val enabled: Boolean = true, - val contentDescription: String = "" + val contentDescription: GetString? = null ) +fun radioOption(value: T, @StringRes title: Int, configure: RadioOptionBuilder.() -> Unit = {}) = + radioOption(value, GetString(title), configure) + +fun radioOption(value: T, title: String, configure: RadioOptionBuilder.() -> Unit = {}) = + radioOption(value, GetString(title), configure) + +fun radioOption(value: T, title: GetString, configure: RadioOptionBuilder.() -> Unit = {}) = + RadioOptionBuilder(value, title).also { it.configure() }.build() + +class RadioOptionBuilder( + val value: T, + val title: GetString +) { + var subtitle: GetString? = null + var enabled: Boolean = true + var contentDescription: GetString? = null + + fun subtitle(string: String) { + subtitle = GetString(string) + } + + fun subtitle(@StringRes stringRes: Int) { + subtitle = GetString(stringRes) + } + + fun contentDescription(string: String) { + contentDescription = GetString(string) + } + + fun contentDescription(@StringRes stringRes: Int) { + contentDescription = GetString(stringRes) + } + + fun build() = RadioOption( + value, + title, + subtitle, + enabled, + contentDescription + ) +} + typealias StringRadioOption = RadioOption typealias ExpirationRadioOption = RadioOption diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Data.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Data.kt index 44ff4a42d8..601f2a9dc7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/Data.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Data.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.ui +import android.content.Context import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource @@ -10,13 +11,17 @@ import androidx.compose.ui.res.stringResource sealed class GetString { @Composable abstract fun string(): String + + abstract fun string(context: Context): String data class FromString(val string: String): GetString() { @Composable override fun string(): String = string + override fun string(context: Context): String = string } data class FromResId(@StringRes val resId: Int): GetString() { @Composable override fun string(): String = stringResource(resId) + override fun string(context: Context): String = context.getString(resId) } }