Convert AppUpdatesSettingsFragment to compose.

This commit is contained in:
Alex Hart
2025-08-20 14:34:21 -03:00
committed by Jeffrey Starke
parent d92286297f
commit 92d31ee6ff
3 changed files with 156 additions and 37 deletions

View File

@@ -6,60 +6,136 @@
package org.thoughtcrime.securesms.components.settings.app.updates
import android.os.Build
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
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.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
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.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.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobs.ApkUpdateJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
/**
* Settings around app updates. Only shown for builds that manage their own app updates.
*/
class AppUpdatesSettingsFragment : DSLSettingsFragment(R.string.preferences_app_updates__title) {
class AppUpdatesSettingsFragment : ComposeFragment() {
override fun bindAdapter(adapter: MappingAdapter) {
adapter.submitList(getConfiguration().toMappingModelList())
private val viewModel: AppUpdatesSettingsViewModel by viewModels()
@Composable
override fun FragmentContent() {
val state by viewModel.state.collectAsStateWithLifecycle()
AppUpdatesSettingsScreen(
state = state,
callbacks = remember { Callbacks() }
)
}
private fun getConfiguration(): DSLConfiguration {
return configure {
override fun onResume() {
super.onResume()
viewModel.refresh()
}
private inner class Callbacks : AppUpdatesSettingsCallbacks {
override fun onNavigationClick() {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
override fun onAutoUpdateChanged(enabled: Boolean) {
SignalStore.apkUpdate.autoUpdate = enabled
viewModel.refresh()
}
override fun onCheckForUpdatesClick() {
AppDependencies.jobManager.add(ApkUpdateJob())
}
}
}
private interface AppUpdatesSettingsCallbacks {
fun onNavigationClick() = Unit
fun onAutoUpdateChanged(enabled: Boolean) = Unit
fun onCheckForUpdatesClick() = Unit
object Empty : AppUpdatesSettingsCallbacks
}
@Composable
private fun AppUpdatesSettingsScreen(
state: AppUpdatesSettingsState,
callbacks: AppUpdatesSettingsCallbacks
) {
Scaffolds.Settings(
title = stringResource(R.string.preferences_app_updates__title),
onNavigationClick = callbacks::onNavigationClick,
navigationIcon = ImageVector.vectorResource(R.drawable.symbol_arrow_start_24)
) { paddingValues ->
LazyColumn(
modifier = Modifier.padding(paddingValues)
) {
if (Build.VERSION.SDK_INT >= 31) {
switchPref(
title = DSLSettingsText.from("Automatic updates"),
summary = DSLSettingsText.from("Automatically download and install app updates"),
isChecked = SignalStore.apkUpdate.autoUpdate,
onClick = {
SignalStore.apkUpdate.autoUpdate = !SignalStore.apkUpdate.autoUpdate
}
item {
Rows.ToggleRow(
checked = state.autoUpdateEnabled,
text = "Automatic updates",
label = "Automatically download and install app updates",
onCheckChanged = callbacks::onAutoUpdateChanged
)
}
}
item {
Rows.TextRow(
text = "Check for updates",
label = "Last checked on: ${rememberLastSuccessfulUpdateString(state.lastCheckedTime)}",
onClick = callbacks::onCheckForUpdatesClick
)
}
clickPref(
title = DSLSettingsText.from("Check for updates"),
summary = DSLSettingsText.from("Last checked on: $lastSuccessfulUpdateString"),
onClick = {
AppDependencies.jobManager.add(ApkUpdateJob())
}
)
}
}
private val lastSuccessfulUpdateString: String
get() {
val lastUpdateTime = SignalStore.apkUpdate.lastSuccessfulCheck
return if (lastUpdateTime > 0) {
val dateFormat = SimpleDateFormat("MMMM dd, yyyy 'at' h:mma", Locale.US)
dateFormat.format(Date(lastUpdateTime))
} else {
"Never"
}
}
}
@Composable
private fun rememberLastSuccessfulUpdateString(lastUpdateTime: Duration): String {
return remember(lastUpdateTime) {
if (lastUpdateTime > Duration.ZERO) {
val dateFormat = SimpleDateFormat("MMMM dd, yyyy 'at' h:mma", Locale.US)
dateFormat.format(Date(lastUpdateTime.inWholeMilliseconds))
} else {
"Never"
}
}
}
@SignalPreview
@Composable
private fun AppUpdatesSettingsScreenPreview() {
Previews.Preview {
AppUpdatesSettingsScreen(
state = AppUpdatesSettingsState(
lastCheckedTime = System.currentTimeMillis().milliseconds,
autoUpdateEnabled = true
),
callbacks = AppUpdatesSettingsCallbacks.Empty
)
}
}

View File

@@ -0,0 +1,13 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.components.settings.app.updates
import kotlin.time.Duration
data class AppUpdatesSettingsState(
val lastCheckedTime: Duration,
val autoUpdateEnabled: Boolean
)

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.components.settings.app.updates
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import org.thoughtcrime.securesms.keyvalue.SignalStore
import kotlin.time.Duration.Companion.milliseconds
class AppUpdatesSettingsViewModel : ViewModel() {
private val internalState = MutableStateFlow(getState())
val state: StateFlow<AppUpdatesSettingsState> = internalState
fun refresh() {
internalState.update { getState() }
}
private fun getState(): AppUpdatesSettingsState {
return AppUpdatesSettingsState(
lastCheckedTime = SignalStore.apkUpdate.lastSuccessfulCheck.milliseconds,
autoUpdateEnabled = SignalStore.apkUpdate.autoUpdate
)
}
}