mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-19 19:38:45 +00:00
Use compose
This commit is contained in:
parent
5142c45643
commit
71b2544c31
@ -166,14 +166,14 @@ dependencies {
|
||||
testImplementation 'org.robolectric:shadows-multidex:4.4'
|
||||
|
||||
implementation 'com.github.bumptech.glide:compose:1.0.0-alpha.1'
|
||||
implementation 'androidx.compose.ui:ui:1.4.3'
|
||||
implementation 'androidx.compose.ui:ui-tooling:1.4.3'
|
||||
implementation 'androidx.compose.ui:ui:1.5.0'
|
||||
implementation 'androidx.compose.ui:ui-tooling:1.5.0'
|
||||
implementation "com.google.accompanist:accompanist-themeadapter-appcompat:0.31.5-beta"
|
||||
implementation "com.google.accompanist:accompanist-pager-indicators:0.31.5-beta"
|
||||
implementation "androidx.compose.runtime:runtime-livedata:1.4.3"
|
||||
implementation "androidx.compose.runtime:runtime-livedata:1.5.0"
|
||||
|
||||
implementation 'androidx.compose.foundation:foundation-layout:1.5.0-alpha02'
|
||||
implementation 'androidx.compose.material:material:1.5.0-alpha02'
|
||||
implementation 'androidx.compose.foundation:foundation-layout:1.5.0'
|
||||
implementation 'androidx.compose.material:material:1.5.0'
|
||||
}
|
||||
|
||||
def canonicalVersionCode = 354
|
||||
|
@ -1,33 +1,58 @@
|
||||
package org.thoughtcrime.securesms.conversation.expiration
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.util.SparseArray
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.OutlinedButton
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.google.android.material.divider.MaterialDividerItemDecoration
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.BuildConfig
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ActivityExpirationSettingsBinding
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
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.ui.AppTheme
|
||||
import org.thoughtcrime.securesms.ui.CellNoMargin
|
||||
import org.thoughtcrime.securesms.ui.GetString
|
||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
|
||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.max
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() {
|
||||
@ -43,34 +68,16 @@ class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() {
|
||||
}
|
||||
|
||||
private val viewModel: ExpirationSettingsViewModel by viewModels {
|
||||
val afterReadOptions = resources.getIntArray(R.array.read_expiration_time_values)
|
||||
.zip(resources.getStringArray(R.array.read_expiration_time_names)) { value, name ->
|
||||
radioOption(ExpiryMode.AfterRead(value.toLong()), name) { contentDescription(R.string.AccessibilityId_time_option) }
|
||||
}
|
||||
val afterSendOptions = resources.getIntArray(R.array.send_expiration_time_values)
|
||||
.zip(resources.getStringArray(R.array.send_expiration_time_names)) { value, name ->
|
||||
radioOption(ExpiryMode.AfterSend(value.toLong()), name) { contentDescription(R.string.AccessibilityId_time_option) }
|
||||
}
|
||||
viewModelFactory.create(threadId, mayAddTestExpiryOption(afterReadOptions), mayAddTestExpiryOption(afterSendOptions))
|
||||
viewModelFactory.create(threadId)
|
||||
}
|
||||
|
||||
private fun mayAddTestExpiryOption(expiryOptions: List<ExpirationRadioOption>): List<ExpirationRadioOption> =
|
||||
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
|
||||
}.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)
|
||||
val scrollParcelArray = SparseArray<Parcelable>()
|
||||
binding.scrollView.saveHierarchyState(scrollParcelArray)
|
||||
outState.putSparseParcelableArray(SCROLL_PARCEL, scrollParcelArray)
|
||||
}
|
||||
// override fun onSaveInstanceState(outState: Bundle) {
|
||||
// super.onSaveInstanceState(outState)
|
||||
// val scrollParcelArray = SparseArray<Parcelable>()
|
||||
// binding.scrollView.saveHierarchyState(scrollParcelArray)
|
||||
// outState.putSparseParcelableArray(SCROLL_PARCEL, scrollParcelArray)
|
||||
// }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||
super.onCreate(savedInstanceState, ready)
|
||||
@ -79,45 +86,35 @@ class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() {
|
||||
|
||||
setUpToolbar()
|
||||
|
||||
savedInstanceState?.let { bundle ->
|
||||
val scrollStateParcel = bundle.getSparseParcelableArray<Parcelable>(SCROLL_PARCEL)
|
||||
if (scrollStateParcel != null) {
|
||||
binding.scrollView.restoreHierarchyState(scrollStateParcel)
|
||||
}
|
||||
}
|
||||
binding.container.setContent { DisappearingMessagesScreen() }
|
||||
|
||||
val deleteTypeOptions = viewModel.getDeleteOptions()
|
||||
val deleteTypeOptionAdapter = RadioOptionAdapter {
|
||||
viewModel.onExpirationTypeSelected(it)
|
||||
}
|
||||
binding.recyclerViewDeleteTypes.apply {
|
||||
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
adapter = deleteTypeOptionAdapter
|
||||
addDividers()
|
||||
setHasFixedSize(true)
|
||||
}
|
||||
deleteTypeOptionAdapter.submitList(deleteTypeOptions)
|
||||
// savedInstanceState?.let { bundle ->
|
||||
// val scrollStateParcel = bundle.getSparseParcelableArray<Parcelable>(SCROLL_PARCEL)
|
||||
// if (scrollStateParcel != null) {
|
||||
// binding.scrollView.restoreHierarchyState(scrollStateParcel)
|
||||
// }
|
||||
// }
|
||||
|
||||
val timerOptionAdapter = RadioOptionAdapter {
|
||||
viewModel.onExpirationTimerSelected(it)
|
||||
}
|
||||
binding.recyclerViewTimerOptions.apply {
|
||||
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
adapter = timerOptionAdapter
|
||||
addDividers()
|
||||
}
|
||||
binding.buttonSet.setOnClickListener {
|
||||
viewModel.onSetClick()
|
||||
}
|
||||
// val deleteTypeOptions = viewModel.getDeleteOptions()
|
||||
|
||||
// binding.buttonSet.setOnClickListener {
|
||||
// viewModel.onSetClick()
|
||||
// }
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
viewModel.uiState.collect { uiState ->
|
||||
binding.textViewDeleteType.isVisible = uiState.showExpirationTypeSelector
|
||||
binding.layoutDeleteTypes.isVisible = uiState.showExpirationTypeSelector
|
||||
binding.textViewFooter.isVisible = uiState.recipient?.isClosedGroupRecipient == true
|
||||
binding.textViewFooter.text = HtmlCompat.fromHtml(getString(R.string.activity_expiration_settings_group_footer), HtmlCompat.FROM_HTML_MODE_COMPACT)
|
||||
viewModel.state.collect { state ->
|
||||
// actionBar?.subtitle = if (state.selectedExpirationType.value is ExpiryMode.AfterSend) {
|
||||
// getString(R.string.activity_expiration_settings_subtitle_sent)
|
||||
// } else {
|
||||
// getString(R.string.activity_expiration_settings_subtitle)
|
||||
// }
|
||||
|
||||
when (uiState.settingsSaved) {
|
||||
// binding.textViewDeleteType.isVisible = state.showExpirationTypeSelector
|
||||
// binding.layoutDeleteTypes.isVisible = state.showExpirationTypeSelector
|
||||
// binding.textViewFooter.isVisible = state.recipient?.isClosedGroupRecipient == true
|
||||
// binding.textViewFooter.text = HtmlCompat.fromHtml(getString(R.string.activity_expiration_settings_group_footer), HtmlCompat.FROM_HTML_MODE_COMPACT)
|
||||
|
||||
when (state.settingsSaved) {
|
||||
true -> {
|
||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@ExpirationSettingsActivity)
|
||||
finish()
|
||||
@ -125,47 +122,39 @@ class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() {
|
||||
false -> showToast(getString(R.string.ExpirationSettingsActivity_settings_not_updated))
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
viewModel.selectedExpirationType.collect { type ->
|
||||
val position = deleteTypeOptions.indexOfFirst { it.value == type }
|
||||
deleteTypeOptionAdapter.setSelectedPosition(max(0, position))
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
viewModel.selectedExpirationTimer.collect { option ->
|
||||
val position =
|
||||
viewModel.expirationTimerOptions.value.indexOfFirst { it.value == option?.value }
|
||||
timerOptionAdapter.setSelectedPosition(max(0, position))
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
viewModel.expirationTimerOptions.collect { options ->
|
||||
binding.textViewTimer.isVisible =
|
||||
options.isNotEmpty() && viewModel.uiState.value.showExpirationTypeSelector
|
||||
binding.layoutTimer.isVisible = options.isNotEmpty()
|
||||
timerOptionAdapter.submitList(options)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun RecyclerView.addDividers() {
|
||||
addItemDecoration(
|
||||
MaterialDividerItemDecoration(
|
||||
this@ExpirationSettingsActivity,
|
||||
RecyclerView.VERTICAL
|
||||
).apply {
|
||||
isLastItemDecorated = false
|
||||
// val position = deleteTypeOptions.indexOfFirst { it.value == state.selectedExpirationType }
|
||||
// deleteTypeOptionAdapter.setSelectedPosition(max(0, position))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
// lifecycleScope.launch {
|
||||
// repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
// viewModel.selectedExpirationType.collect { type ->
|
||||
// val position = deleteTypeOptions.indexOfFirst { it.value == type }
|
||||
// deleteTypeOptionAdapter.setSelectedPosition(max(0, position))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// lifecycleScope.launch {
|
||||
// repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
// viewModel.selectedExpirationTimer.collect { option ->
|
||||
// val position =
|
||||
// viewModel.expirationTimerOptions.value.indexOfFirst { it.value == option?.value }
|
||||
// timerOptionAdapter.setSelectedPosition(max(0, position))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// lifecycleScope.launch {
|
||||
// repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
// viewModel.expirationTimerOptions.collect { options ->
|
||||
// binding.textViewTimer.isVisible =
|
||||
// options.isNotEmpty() && viewModel.uiState.value.showExpirationTypeSelector
|
||||
// binding.layoutTimer.isVisible = options.isNotEmpty()
|
||||
// timerOptionAdapter.submitList(options)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private fun showToast(message: String) {
|
||||
@ -176,11 +165,6 @@ class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() {
|
||||
setSupportActionBar(binding.toolbar)
|
||||
val actionBar = supportActionBar ?: return
|
||||
actionBar.title = getString(R.string.activity_expiration_settings_title)
|
||||
actionBar.subtitle = if (viewModel.selectedExpirationType.value is ExpiryMode.AfterSend) {
|
||||
getString(R.string.activity_expiration_settings_subtitle_sent)
|
||||
} else {
|
||||
getString(R.string.activity_expiration_settings_subtitle)
|
||||
}
|
||||
actionBar.setDisplayHomeAsUpEnabled(true)
|
||||
actionBar.setHomeButtonEnabled(true)
|
||||
}
|
||||
@ -190,4 +174,139 @@ class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() {
|
||||
const val THREAD_ID = "thread_id"
|
||||
}
|
||||
|
||||
}
|
||||
@Composable
|
||||
fun DisappearingMessagesScreen() {
|
||||
val uiState by viewModel.uiState.collectAsState(UiState())
|
||||
AppTheme {
|
||||
DisappearingMessages(uiState, onSetClick = viewModel::onSetClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DisappearingMessages(
|
||||
state: UiState,
|
||||
modifier: Modifier = Modifier,
|
||||
onSetClick: () -> Unit = {}
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 32.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
state.cards.filter { it.options.isNotEmpty() }.forEach { OptionsCard(it) }
|
||||
}
|
||||
|
||||
Gradient(100.dp, modifier = Modifier.align(Alignment.BottomCenter))
|
||||
}
|
||||
|
||||
OutlineButton(
|
||||
stringResource(R.string.expiration_settings_set_button_title),
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(bottom = 20.dp),
|
||||
onClick = onSetClick
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OptionsCard(card: CardModel) {
|
||||
Text(text = card.title())
|
||||
CellNoMargin {
|
||||
LazyColumn(
|
||||
modifier = Modifier.heightIn(max = 5000.dp)
|
||||
) {
|
||||
items(card.options) {
|
||||
TitledRadioButton(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Gradient(height: Dp, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(height)
|
||||
.background(
|
||||
brush = Brush.verticalGradient(
|
||||
colors = listOf(Color.Transparent, MaterialTheme.colors.primary),
|
||||
startY = 0f,
|
||||
endY = height.value
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TitledRadioButton(option: OptionModel) {
|
||||
Row(modifier = Modifier
|
||||
.heightIn(min = 60.dp)
|
||||
.padding(horizontal = 34.dp)) {
|
||||
Column(modifier = Modifier
|
||||
.weight(1f)
|
||||
.align(Alignment.CenterVertically)) {
|
||||
Column {
|
||||
Text(text = option.title())
|
||||
option.subtitle?.let { Text(text = it()) }
|
||||
}
|
||||
}
|
||||
RadioButton(
|
||||
selected = option.selected,
|
||||
onClick = option.onClick,
|
||||
modifier = Modifier
|
||||
.height(26.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OutlineButton(text: String, modifier: Modifier = Modifier, onClick: () -> Unit) {
|
||||
OutlinedButton(
|
||||
modifier = modifier.size(108.dp, 34.dp),
|
||||
onClick = onClick,
|
||||
border = BorderStroke(1.dp, MaterialTheme.colors.secondary),
|
||||
shape = RoundedCornerShape(50), // = 50% percent
|
||||
colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colors.secondary)
|
||||
){
|
||||
Text(text = text)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewMessageDetails(
|
||||
@PreviewParameter(ThemeResPreviewParameterProvider::class) themeResId: Int
|
||||
) {
|
||||
PreviewTheme(themeResId) {
|
||||
DisappearingMessages(
|
||||
UiState(
|
||||
cards = listOf(
|
||||
CardModel(GetString(R.string.activity_expiration_settings_delete_type), typeOptions()),
|
||||
CardModel(GetString(R.string.activity_expiration_settings_timer), timeOptions())
|
||||
)
|
||||
),
|
||||
modifier = Modifier.size(400.dp, 600.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun typeOptions() = listOf(
|
||||
OptionModel(GetString(R.string.expiration_off)),
|
||||
OptionModel(GetString(R.string.expiration_type_disappear_legacy)),
|
||||
OptionModel(GetString(R.string.expiration_type_disappear_after_read)),
|
||||
OptionModel(GetString(R.string.expiration_type_disappear_after_send))
|
||||
)
|
||||
|
||||
fun timeOptions() = listOf(
|
||||
OptionModel(GetString("1 Minute")),
|
||||
OptionModel(GetString("5 Minutes")),
|
||||
OptionModel(GetString("1 Week")),
|
||||
OptionModel(GetString("2 Weeks")),
|
||||
)
|
@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms.conversation.expiration
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@ -7,36 +8,31 @@ 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
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.BuildConfig
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.session.libsession.messaging.messages.ExpirationConfiguration
|
||||
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||
import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsession.utilities.GroupRecord
|
||||
import org.session.libsession.utilities.ExpirationUtil
|
||||
import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsignal.utilities.guava.Optional
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase
|
||||
import org.thoughtcrime.securesms.database.Storage
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.preferences.ExpirationRadioOption
|
||||
import org.thoughtcrime.securesms.preferences.RadioOption
|
||||
import org.thoughtcrime.securesms.preferences.radioOption
|
||||
import kotlin.reflect.KClass
|
||||
import org.thoughtcrime.securesms.ui.GetString
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ExpirationSettingsViewModel(
|
||||
private val threadId: Long,
|
||||
private val afterReadOptions: List<ExpirationRadioOption>,
|
||||
private val afterSendOptions: List<ExpirationRadioOption>,
|
||||
private val textSecurePreferences: TextSecurePreferences,
|
||||
private val messageExpirationManager: MessageExpirationManagerProtocol,
|
||||
private val threadDb: ThreadDatabase,
|
||||
@ -44,47 +40,38 @@ class ExpirationSettingsViewModel(
|
||||
private val storage: Storage
|
||||
) : ViewModel() {
|
||||
|
||||
private val _uiState = MutableStateFlow(ExpirationSettingsUiState())
|
||||
val uiState: StateFlow<ExpirationSettingsUiState> = _uiState
|
||||
private val _state = MutableStateFlow(State())
|
||||
val state = _state.asStateFlow()
|
||||
|
||||
val uiState = _state.map {
|
||||
UiState(
|
||||
cards = listOf(
|
||||
CardModel(GetString(R.string.activity_expiration_settings_delete_type), typeOptions()),
|
||||
CardModel(GetString(R.string.activity_expiration_settings_timer), timeOptions(it))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private var expirationConfig: ExpirationConfiguration? = null
|
||||
|
||||
private val _selectedExpirationType: MutableStateFlow<ExpiryMode> = MutableStateFlow(ExpiryMode.NONE)
|
||||
val selectedExpirationType: StateFlow<ExpiryMode> = _selectedExpirationType
|
||||
|
||||
private val _selectedExpirationTimer = MutableStateFlow(afterSendOptions.firstOrNull())
|
||||
val selectedExpirationTimer: StateFlow<RadioOption<ExpiryMode>?> = _selectedExpirationTimer
|
||||
|
||||
private val _expirationTimerOptions = MutableStateFlow<List<RadioOption<ExpiryMode>>>(emptyList())
|
||||
val expirationTimerOptions: StateFlow<List<RadioOption<ExpiryMode>>> = _expirationTimerOptions
|
||||
|
||||
init {
|
||||
// SETUP
|
||||
viewModelScope.launch {
|
||||
expirationConfig = storage.getExpirationConfiguration(threadId)
|
||||
val expirationType = expirationConfig?.expiryMode
|
||||
val expiryMode = expirationConfig?.expiryMode ?: ExpiryMode.NONE
|
||||
val recipient = threadDb.getRecipientForThreadId(threadId)
|
||||
val groupInfo = recipient?.takeIf { it.isClosedGroupRecipient }
|
||||
?.run { address.toGroupString().let(groupDb::getGroup).orNull() }
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
_state.update { state ->
|
||||
state.copy(
|
||||
isSelfAdmin = groupInfo == null || groupInfo.admins.any{ it.serialize() == textSecurePreferences.getLocalNumber() },
|
||||
showExpirationTypeSelector = true,
|
||||
recipient = recipient
|
||||
recipient = recipient,
|
||||
expiryType = expiryMode.type,
|
||||
expiryMode = expiryMode
|
||||
)
|
||||
}
|
||||
_selectedExpirationType.value = if (ExpirationConfiguration.isNewConfigEnabled) {
|
||||
expirationType ?: ExpiryMode.NONE
|
||||
} else {
|
||||
if (expirationType != null && expirationType != ExpiryMode.NONE)
|
||||
ExpiryMode.Legacy(expirationType.expirySeconds)
|
||||
else ExpiryMode.NONE
|
||||
}
|
||||
_selectedExpirationTimer.value = when(expirationType) {
|
||||
is ExpiryMode.AfterSend -> afterSendOptions.find { it.value == expirationType }
|
||||
is ExpiryMode.AfterRead -> afterReadOptions.find { it.value == expirationType }
|
||||
else -> afterSendOptions.firstOrNull()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// selectedExpirationType.mapLatest {
|
||||
// when (it) {
|
||||
@ -100,146 +87,99 @@ class ExpirationSettingsViewModel(
|
||||
// }.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
fun onExpirationTypeSelected(option: RadioOption<ExpiryMode>) {
|
||||
_selectedExpirationType.value = option.value
|
||||
_selectedExpirationTimer.value = _expirationTimerOptions.value.firstOrNull()
|
||||
fun typeOption(
|
||||
type: ExpiryType,
|
||||
@StringRes title: Int,
|
||||
@StringRes subtitle: Int,
|
||||
// @StringRes contentDescription: Int
|
||||
) = OptionModel(GetString(title), GetString(subtitle)) { setType(type) }
|
||||
|
||||
private fun typeOptions() = listOf(
|
||||
typeOption(ExpiryType.NONE, R.string.expiration_off, R.string.AccessibilityId_disable_disappearing_messages),
|
||||
typeOption(ExpiryType.LEGACY, R.string.expiration_type_disappear_legacy, R.string.expiration_type_disappear_legacy_description),
|
||||
typeOption(ExpiryType.AFTER_READ, R.string.expiration_type_disappear_after_read, R.string.expiration_type_disappear_after_read_description),
|
||||
typeOption(ExpiryType.AFTER_SEND, R.string.expiration_type_disappear_after_send, R.string.expiration_type_disappear_after_send_description),
|
||||
)
|
||||
|
||||
private fun setType(type: ExpiryType) {
|
||||
_state.update { it.copy(expiryType = type, expiryMode = type.mode(0)) }
|
||||
}
|
||||
|
||||
fun onExpirationTimerSelected(option: RadioOption<ExpiryMode>) {
|
||||
_selectedExpirationTimer.value = option
|
||||
private fun setTime(seconds: Long) {
|
||||
_state.update { it.copy(
|
||||
expiryMode = it.expiryType.mode(seconds)
|
||||
) }
|
||||
}
|
||||
|
||||
private fun KClass<out ExpiryMode>?.withTime(expirationTimer: Long) = when(this) {
|
||||
ExpiryMode.AfterRead::class -> ExpiryMode.AfterRead(expirationTimer)
|
||||
ExpiryMode.AfterSend::class -> ExpiryMode.AfterSend(expirationTimer)
|
||||
else -> ExpiryMode.NONE
|
||||
private fun setMode(mode: ExpiryMode) {
|
||||
_state.update { it.copy(
|
||||
expiryMode = mode
|
||||
) }
|
||||
}
|
||||
|
||||
fun timeOption(seconds: Long, @StringRes id: Int) = OptionModel(GetString(id), selected = false, onClick = { setTime(seconds) })
|
||||
fun timeOption(seconds: Long, title: String, subtitle: String) = OptionModel(GetString(title), GetString(subtitle), selected = false, onClick = { setTime(seconds) })
|
||||
|
||||
// private fun timeOptions(state: State) = timeOptions(state.types.isEmpty(), state.expiryType == ExpiryType.AFTER_SEND)
|
||||
private fun timeOptions(state: State) = noteToSelfOptions()
|
||||
|
||||
val afterReadTimes = listOf(12.hours, 1.days, 7.days, 14.days)
|
||||
val afterSendTimes = listOf(5.minutes, 1.hours) + afterReadTimes
|
||||
|
||||
private fun noteToSelfOptions() = listOfNotNull(
|
||||
typeOption(ExpiryType.NONE, R.string.arrays__off, R.string.arrays__off),
|
||||
noteToSelfOption(1.minutes, subtitle = "for testing purposes").takeIf { BuildConfig.DEBUG },
|
||||
) + afterSendTimes.map(::noteToSelfOption)
|
||||
|
||||
private fun noteToSelfOption(
|
||||
duration: Duration,
|
||||
title: GetString = GetString { ExpirationUtil.getExpirationDisplayValue(it, duration.inWholeSeconds.toInt()) },
|
||||
subtitle: String? = null
|
||||
) = OptionModel(
|
||||
title = title,
|
||||
subtitle = subtitle?.let(::GetString),
|
||||
selected = false,
|
||||
onClick = { setMode(ExpiryMode.AfterSend(duration.inWholeSeconds)) }
|
||||
)
|
||||
|
||||
fun onSetClick() = viewModelScope.launch {
|
||||
val state = uiState.value
|
||||
val expiryMode = _selectedExpirationTimer.value?.value ?: ExpiryMode.NONE
|
||||
val typeValue = expiryMode.let {
|
||||
if (it is ExpiryMode.Legacy) ExpiryMode.AfterRead(it.expirySeconds)
|
||||
else it
|
||||
}
|
||||
val state = _state.value
|
||||
// val expiryMode = _selectedExpirationTimer.value?.value ?: ExpiryMode.NONE
|
||||
// val typeValue = expiryMode.let {
|
||||
// if (it is ExpiryMode.Legacy) ExpiryMode.AfterRead(it.expirySeconds)
|
||||
// else it
|
||||
// }
|
||||
val address = state.recipient?.address
|
||||
if (address == null || expirationConfig?.expiryMode == typeValue) {
|
||||
_uiState.update {
|
||||
it.copy(settingsSaved = false)
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
// if (address == null || expirationConfig?.expiryMode == typeValue) {
|
||||
// _state.update {
|
||||
// it.copy(settingsSaved = false)
|
||||
// }
|
||||
// return@launch
|
||||
// }
|
||||
|
||||
val expiryChangeTimestampMs = SnodeAPI.nowWithOffset
|
||||
storage.setExpirationConfiguration(ExpirationConfiguration(threadId, typeValue, expiryChangeTimestampMs))
|
||||
|
||||
val message = ExpirationTimerUpdate(typeValue.expirySeconds.toInt())
|
||||
message.sender = textSecurePreferences.getLocalNumber()
|
||||
message.recipient = address.serialize()
|
||||
message.sentTimestamp = expiryChangeTimestampMs
|
||||
messageExpirationManager.setExpirationTimer(message, typeValue)
|
||||
|
||||
MessageSender.send(message, address)
|
||||
_uiState.update {
|
||||
it.copy(settingsSaved = true)
|
||||
}
|
||||
// val expiryChangeTimestampMs = SnodeAPI.nowWithOffset
|
||||
// storage.setExpirationConfiguration(ExpirationConfiguration(threadId, typeValue, expiryChangeTimestampMs))
|
||||
//
|
||||
// val message = ExpirationTimerUpdate(typeValue.expirySeconds.toInt())
|
||||
// message.sender = textSecurePreferences.getLocalNumber()
|
||||
// message.recipient = address.serialize()
|
||||
// message.sentTimestamp = expiryChangeTimestampMs
|
||||
// messageExpirationManager.setExpirationTimer(message, typeValue)
|
||||
//
|
||||
// MessageSender.send(message, address)
|
||||
// state.update {
|
||||
// it.copy(settingsSaved = true)
|
||||
// }
|
||||
}
|
||||
|
||||
fun getDeleteOptions(): List<ExpirationRadioOption> {
|
||||
if (!uiState.value.showExpirationTypeSelector) return emptyList()
|
||||
|
||||
val recipient = uiState.value.recipient ?: return emptyList()
|
||||
|
||||
return if (ExpirationConfiguration.isNewConfigEnabled) when {
|
||||
recipient.isLocalNumber -> noteToSelfOptions()
|
||||
recipient.isContactRecipient -> contactRecipientOptions()
|
||||
recipient.isClosedGroupRecipient -> closedGroupRecipientOptions()
|
||||
else -> emptyList()
|
||||
} else when {
|
||||
recipient.isContactRecipient && !recipient.isLocalNumber -> oldConfigContactRecipientOptions()
|
||||
else -> oldConfigDefaultOptions()
|
||||
}
|
||||
}
|
||||
|
||||
private fun oldConfigDefaultOptions() = listOf(
|
||||
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)
|
||||
}
|
||||
)
|
||||
|
||||
private fun oldConfigContactRecipientOptions() = listOf(
|
||||
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)
|
||||
}
|
||||
)
|
||||
|
||||
private fun contactRecipientOptions() = listOf(
|
||||
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)
|
||||
}
|
||||
)
|
||||
|
||||
private fun closedGroupRecipientOptions() = listOf(
|
||||
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)
|
||||
}
|
||||
)
|
||||
|
||||
private fun noteToSelfOptions() = listOf(
|
||||
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)
|
||||
}
|
||||
)
|
||||
|
||||
@dagger.assisted.AssistedFactory
|
||||
interface AssistedFactory {
|
||||
fun create(
|
||||
threadId: Long,
|
||||
@Assisted("afterRead") afterReadOptions: List<ExpirationRadioOption>,
|
||||
@Assisted("afterSend") afterSendOptions: List<ExpirationRadioOption>
|
||||
): Factory
|
||||
fun create(threadId: Long): Factory
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class Factory @AssistedInject constructor(
|
||||
@Assisted private val threadId: Long,
|
||||
@Assisted("afterRead") private val afterReadOptions: List<ExpirationRadioOption>,
|
||||
@Assisted("afterSend") private val afterSendOptions: List<ExpirationRadioOption>,
|
||||
private val textSecurePreferences: TextSecurePreferences,
|
||||
private val messageExpirationManager: MessageExpirationManagerProtocol,
|
||||
private val threadDb: ThreadDatabase,
|
||||
@ -247,24 +187,54 @@ class ExpirationSettingsViewModel(
|
||||
private val storage: Storage
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ExpirationSettingsViewModel(
|
||||
threadId,
|
||||
afterReadOptions,
|
||||
afterSendOptions,
|
||||
textSecurePreferences,
|
||||
messageExpirationManager,
|
||||
threadDb,
|
||||
groupDb,
|
||||
storage
|
||||
) as T
|
||||
}
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = ExpirationSettingsViewModel(
|
||||
threadId,
|
||||
textSecurePreferences,
|
||||
messageExpirationManager,
|
||||
threadDb,
|
||||
groupDb,
|
||||
storage
|
||||
) as T
|
||||
}
|
||||
}
|
||||
|
||||
data class ExpirationSettingsUiState(
|
||||
data class State(
|
||||
val isSelfAdmin: Boolean = false,
|
||||
val showExpirationTypeSelector: Boolean = false,
|
||||
val settingsSaved: Boolean? = null,
|
||||
val recipient: Recipient? = null
|
||||
val recipient: Recipient? = null,
|
||||
val expiryType: ExpiryType = ExpiryType.NONE,
|
||||
val expiryMode: ExpiryMode? = null,
|
||||
val types: List<ExpiryType> = emptyList()
|
||||
) {
|
||||
val isSelf = recipient?.isLocalNumber == true
|
||||
}
|
||||
|
||||
data class UiState(
|
||||
val cards: List<CardModel> = emptyList()
|
||||
)
|
||||
|
||||
data class CardModel(
|
||||
val title: GetString,
|
||||
val options: List<OptionModel>
|
||||
)
|
||||
|
||||
data class OptionModel(
|
||||
val title: GetString,
|
||||
val subtitle: GetString? = null,
|
||||
val selected: Boolean = false,
|
||||
val onClick: () -> Unit = {}
|
||||
)
|
||||
|
||||
enum class ExpiryType(val mode: (Long) -> ExpiryMode) {
|
||||
NONE({ ExpiryMode.NONE }),
|
||||
LEGACY(ExpiryMode::Legacy),
|
||||
AFTER_SEND(ExpiryMode::AfterSend),
|
||||
AFTER_READ(ExpiryMode::AfterRead)
|
||||
}
|
||||
|
||||
private val ExpiryMode.type: ExpiryType get() = when(this) {
|
||||
is ExpiryMode.Legacy -> ExpiryType.LEGACY
|
||||
is ExpiryMode.AfterSend -> ExpiryType.AFTER_SEND
|
||||
is ExpiryMode.AfterRead -> ExpiryType.AFTER_READ
|
||||
else -> ExpiryType.NONE
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class AlbumThumbnailView : RelativeLayout {
|
||||
private var slides: List<Slide> = listOf()
|
||||
private var slideSize: Int = 0
|
||||
|
||||
override fun dispatchDraw(canvas: Canvas?) {
|
||||
override fun dispatchDraw(canvas: Canvas) {
|
||||
super.dispatchDraw(canvas)
|
||||
cornerMask.mask(canvas)
|
||||
}
|
||||
|
@ -30,9 +30,7 @@ class ThumbnailProgressBar: View {
|
||||
private val objectRect = Rect()
|
||||
private val drawingRect = Rect()
|
||||
|
||||
override fun dispatchDraw(canvas: Canvas?) {
|
||||
if (canvas == null) return
|
||||
|
||||
override fun dispatchDraw(canvas: Canvas) {
|
||||
getDrawingRect(objectRect)
|
||||
drawingRect.set(objectRect)
|
||||
|
||||
|
@ -3,12 +3,18 @@ package org.thoughtcrime.securesms.ui
|
||||
import android.content.Context
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
||||
/**
|
||||
* Compatibility class to allow ViewModels to use strings and string resources interchangeably.
|
||||
*/
|
||||
sealed class GetString {
|
||||
|
||||
@Composable
|
||||
operator fun invoke() = string()
|
||||
operator fun invoke(context: Context) = string(context)
|
||||
|
||||
@Composable
|
||||
abstract fun string(): String
|
||||
|
||||
@ -22,12 +28,17 @@ sealed class GetString {
|
||||
@Composable
|
||||
override fun string(): String = stringResource(resId)
|
||||
override fun string(context: Context): String = context.getString(resId)
|
||||
|
||||
}
|
||||
data class FromFun(val function: (Context) -> String): GetString() {
|
||||
@Composable
|
||||
override fun string(): String = function(LocalContext.current)
|
||||
override fun string(context: Context): String = function(context)
|
||||
}
|
||||
}
|
||||
|
||||
fun GetString(@StringRes resId: Int) = GetString.FromResId(resId)
|
||||
fun GetString(string: String) = GetString.FromString(string)
|
||||
fun GetString(function: (Context) -> String) = GetString.FromFun(function)
|
||||
|
||||
|
||||
/**
|
||||
|
@ -22,101 +22,11 @@
|
||||
app:subtitleTextAppearance="@style/TextAppearance.Session.ToolbarSubtitle"
|
||||
app:title="@string/activity_expiration_settings_title" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_container"
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/toolbar"
|
||||
android:layout_marginBottom="@dimen/massive_spacing"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_view_delete_type"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="@dimen/very_large_spacing"
|
||||
android:paddingVertical="@dimen/small_spacing"
|
||||
android:text="@string/activity_expiration_settings_delete_type"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="@dimen/medium_font_size" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_delete_types"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/small_spacing"
|
||||
android:background="@drawable/preference_single"
|
||||
android:paddingHorizontal="@dimen/large_spacing"
|
||||
android:paddingVertical="@dimen/medium_spacing">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:overScrollMode="never"
|
||||
android:id="@+id/recycler_view_delete_types"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/item_selectable" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_view_timer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="@dimen/very_large_spacing"
|
||||
android:paddingVertical="@dimen/small_spacing"
|
||||
android:text="@string/activity_expiration_settings_timer"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="@dimen/medium_font_size" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_timer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/AccessibilityId_disappearing_messages_timer"
|
||||
android:layout_marginHorizontal="@dimen/small_spacing"
|
||||
android:background="@drawable/preference_single"
|
||||
android:paddingHorizontal="@dimen/large_spacing"
|
||||
android:paddingVertical="@dimen/medium_spacing">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:overScrollMode="never"
|
||||
android:id="@+id/recycler_view_timer_options"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/item_selectable" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_view_footer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="@dimen/very_large_spacing"
|
||||
android:paddingVertical="@dimen/small_spacing"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/activity_expiration_settings_group_footer"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_set"
|
||||
style="@style/Widget.Session.Button.Common.ProminentOutline"
|
||||
android:layout_width="196dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:contentDescription="@string/AccessibilityId_set_button"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginBottom="@dimen/very_large_spacing"
|
||||
android:text="@string/expiration_settings_set_button_title" />
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/toolbar"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
@ -228,40 +228,4 @@
|
||||
<item>@string/notify_type_mentions</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="read_expiration_time_names">
|
||||
<item>@string/arrays__off</item>
|
||||
<item>@string/arrays__five_minutes</item>
|
||||
<item>@string/arrays__one_hour</item>
|
||||
<item>@string/arrays__twelve_hours</item>
|
||||
<item>@string/arrays__one_day</item>
|
||||
<item>@string/arrays__one_week</item>
|
||||
<item>@string/arrays__two_weeks</item>
|
||||
</integer-array>
|
||||
|
||||
<integer-array name="read_expiration_time_values">
|
||||
<item>0</item>
|
||||
<item>300</item>
|
||||
<item>3600</item>
|
||||
<item>43200</item>
|
||||
<item>86400</item>
|
||||
<item>604800</item>
|
||||
<item>1209600</item>
|
||||
</integer-array>
|
||||
|
||||
<integer-array name="send_expiration_time_names">
|
||||
<item>@string/arrays__off</item>
|
||||
<item>@string/arrays__twelve_hours</item>
|
||||
<item>@string/arrays__one_day</item>
|
||||
<item>@string/arrays__one_week</item>
|
||||
<item>@string/arrays__two_weeks</item>
|
||||
</integer-array>
|
||||
|
||||
<integer-array name="send_expiration_time_values">
|
||||
<item>0</item>
|
||||
<item>43200</item>
|
||||
<item>86400</item>
|
||||
<item>604800</item>
|
||||
<item>1209600</item>
|
||||
</integer-array>
|
||||
|
||||
</resources>
|
||||
|
@ -57,6 +57,6 @@ allprojects {
|
||||
project.ext {
|
||||
androidMinimumSdkVersion = 23
|
||||
androidTargetSdkVersion = 33
|
||||
androidCompileSdkVersion = 33
|
||||
androidCompileSdkVersion = 34
|
||||
}
|
||||
}
|
@ -161,7 +161,7 @@ private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimer
|
||||
val type = when {
|
||||
recipient.isContactRecipient -> ExpiryMode.AfterRead(message.duration!!.toLong())
|
||||
recipient.isGroupRecipient -> ExpiryMode.AfterSend(message.duration!!.toLong())
|
||||
else -> null
|
||||
else -> ExpiryMode.NONE
|
||||
}
|
||||
try {
|
||||
var threadId: Long = module.storage.getOrCreateThreadIdFor(fromSerialized(message.sender!!))
|
||||
@ -295,7 +295,7 @@ fun MessageReceiver.updateExpiryIfNeeded(
|
||||
val durationSeconds = if (proto.hasExpirationTimer()) proto.expirationTimer else 0
|
||||
val type = if (proto.hasExpirationType()) proto.expirationType else null
|
||||
|
||||
val expiryMode = type?.expiryMode(durationSeconds.toLong())
|
||||
val expiryMode = type?.expiryMode(durationSeconds.toLong()) ?: ExpiryMode.NONE
|
||||
|
||||
val remoteConfig = ExpirationConfiguration(
|
||||
threadID,
|
||||
@ -325,12 +325,12 @@ fun MessageReceiver.updateExpiryIfNeeded(
|
||||
|
||||
// handle a delete after send expired fetch
|
||||
if (type == ExpirationType.DELETE_AFTER_SEND
|
||||
&& sentTime + (configToUse.expiryMode?.expirySeconds ?: 0) <= SnodeAPI.nowWithOffset) {
|
||||
&& sentTime + configToUse.expiryMode.expirySeconds <= SnodeAPI.nowWithOffset) {
|
||||
throw MessageReceiver.Error.ExpiredMessage
|
||||
}
|
||||
// handle a delete after read last known config value
|
||||
if (type == ExpirationType.DELETE_AFTER_READ
|
||||
&& sentTime + (configToUse.expiryMode?.expirySeconds ?: 0) <= storage.getLastSeen(threadID)) {
|
||||
&& sentTime + configToUse.expiryMode.expirySeconds <= storage.getLastSeen(threadID)) {
|
||||
throw MessageReceiver.Error.ExpiredMessage
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user