Compare commits
2
Commits
889c57bbba
...
bafebfd253
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bafebfd253 | ||
|
|
f19d65abb5 |
@@ -6,6 +6,9 @@ import agzam.vyatsu.ui.groups.GroupPickerViewModel
|
|||||||
import agzam.vyatsu.ui.home.HomeScreen
|
import agzam.vyatsu.ui.home.HomeScreen
|
||||||
import agzam.vyatsu.ui.schedule.ScheduleViewModel
|
import agzam.vyatsu.ui.schedule.ScheduleViewModel
|
||||||
import agzam.vyatsu.ui.settings.UpdateViewModel
|
import agzam.vyatsu.ui.settings.UpdateViewModel
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -24,6 +27,30 @@ fun AppRoot() {
|
|||||||
val updateViewModel: UpdateViewModel = viewModel(factory = updateFactory(container))
|
val updateViewModel: UpdateViewModel = viewModel(factory = updateFactory(container))
|
||||||
|
|
||||||
val state by scheduleViewModel.uiState.collectAsState()
|
val state by scheduleViewModel.uiState.collectAsState()
|
||||||
|
val showFirstRunDialog by updateViewModel.showFirstRunDialog.collectAsState()
|
||||||
|
|
||||||
|
if (showFirstRunDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { updateViewModel.onFirstRunDecision(false) },
|
||||||
|
title = { Text("Автоматические обновления") },
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
"Разрешить автоматически проверять и устанавливать " +
|
||||||
|
"новые версии приложения?"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = { updateViewModel.onFirstRunDecision(true) }) {
|
||||||
|
Text("Разрешить")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { updateViewModel.onFirstRunDecision(false) }) {
|
||||||
|
Text("Не надо")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (state.noGroup) {
|
if (state.noGroup) {
|
||||||
GroupPickerScreen(
|
GroupPickerScreen(
|
||||||
|
|||||||
@@ -33,4 +33,10 @@ interface PreferencesDao {
|
|||||||
|
|
||||||
@Query("INSERT OR REPLACE INTO preferences(key, value) VALUES('autoUpdateEnabled', :enabled)")
|
@Query("INSERT OR REPLACE INTO preferences(key, value) VALUES('autoUpdateEnabled', :enabled)")
|
||||||
suspend fun setAutoUpdateEnabled(enabled: String)
|
suspend fun setAutoUpdateEnabled(enabled: String)
|
||||||
|
|
||||||
|
@Query("SELECT value FROM preferences WHERE key = 'autoUpdateAsked'")
|
||||||
|
suspend fun autoUpdateAsked(): String?
|
||||||
|
|
||||||
|
@Query("INSERT OR REPLACE INTO preferences(key, value) VALUES('autoUpdateAsked', :asked)")
|
||||||
|
suspend fun setAutoUpdateAsked(asked: String)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ class PreferencesRepositoryImpl(
|
|||||||
override suspend fun selectedDayDate(): String? = dao.selectedDayDate()
|
override suspend fun selectedDayDate(): String? = dao.selectedDayDate()
|
||||||
override suspend fun setSelectedDayDate(date: String) = dao.setSelectedDayDate(date)
|
override suspend fun setSelectedDayDate(date: String) = dao.setSelectedDayDate(date)
|
||||||
|
|
||||||
override suspend fun autoUpdateEnabled(): Boolean = dao.autoUpdateEnabled() != "false"
|
override suspend fun autoUpdateEnabled(): Boolean = dao.autoUpdateEnabled() == "true"
|
||||||
override suspend fun setAutoUpdateEnabled(enabled: Boolean) = dao.setAutoUpdateEnabled(enabled.toString())
|
override suspend fun setAutoUpdateEnabled(enabled: Boolean) = dao.setAutoUpdateEnabled(enabled.toString())
|
||||||
|
|
||||||
|
override suspend fun autoUpdateAsked(): Boolean = dao.autoUpdateAsked() == "true"
|
||||||
|
override suspend fun setAutoUpdateAsked(asked: Boolean) = dao.setAutoUpdateAsked(asked.toString())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,4 +11,7 @@ interface PreferencesRepository {
|
|||||||
|
|
||||||
suspend fun autoUpdateEnabled(): Boolean
|
suspend fun autoUpdateEnabled(): Boolean
|
||||||
suspend fun setAutoUpdateEnabled(enabled: Boolean)
|
suspend fun setAutoUpdateEnabled(enabled: Boolean)
|
||||||
|
|
||||||
|
suspend fun autoUpdateAsked(): Boolean
|
||||||
|
suspend fun setAutoUpdateAsked(asked: Boolean)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,12 +50,12 @@ fun SettingsScreen(viewModel: UpdateViewModel) {
|
|||||||
item {
|
item {
|
||||||
SettingsItem(
|
SettingsItem(
|
||||||
title = "Проверить наличие обновлений",
|
title = "Проверить наличие обновлений",
|
||||||
subtitle = updateStatusText(updateState),
|
subtitle = updateStatusText(updateState, autoEnabled),
|
||||||
onClick = { viewModel.checkNow() }
|
onClick = { viewModel.checkNow() }
|
||||||
)
|
)
|
||||||
SettingsDivider()
|
SettingsDivider()
|
||||||
}
|
}
|
||||||
if (updateState is UpdateState.Available || updateState is UpdateState.ReadyToInstall) {
|
if (autoEnabled && (updateState is UpdateState.Available || updateState is UpdateState.ReadyToInstall)) {
|
||||||
item {
|
item {
|
||||||
SettingsItem(
|
SettingsItem(
|
||||||
title = "Установить обновление",
|
title = "Установить обновление",
|
||||||
@@ -89,11 +89,13 @@ fun SettingsScreen(viewModel: UpdateViewModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateStatusText(state: UpdateState): String = when (state) {
|
private fun updateStatusText(state: UpdateState, autoEnabled: Boolean): String = when (state) {
|
||||||
is UpdateState.Idle -> "Проверить сейчас"
|
is UpdateState.Idle -> "Проверить сейчас"
|
||||||
is UpdateState.Checking -> "Проверка…"
|
is UpdateState.Checking -> "Проверка…"
|
||||||
is UpdateState.UpToDate -> "Установлена последняя версия"
|
is UpdateState.UpToDate -> "Установлена последняя версия"
|
||||||
is UpdateState.Available -> "Доступна версия ${state.version}"
|
is UpdateState.Available ->
|
||||||
|
if (autoEnabled) "Доступна версия ${state.version}"
|
||||||
|
else "Доступна версия ${state.version} — включите автообновления для установки"
|
||||||
is UpdateState.Downloading -> "Скачивание: ${state.progress?.let { "$it%" } ?: "…"}"
|
is UpdateState.Downloading -> "Скачивание: ${state.progress?.let { "$it%" } ?: "…"}"
|
||||||
is UpdateState.ReadyToInstall -> "Обновление скачано, нажмите для установки"
|
is UpdateState.ReadyToInstall -> "Обновление скачано, нажмите для установки"
|
||||||
is UpdateState.Failed -> "Ошибка: ${state.message}"
|
is UpdateState.Failed -> "Ошибка: ${state.message}"
|
||||||
|
|||||||
@@ -17,18 +17,33 @@ class UpdateViewModel(
|
|||||||
|
|
||||||
val updateState: StateFlow<UpdateState> = updater.state
|
val updateState: StateFlow<UpdateState> = updater.state
|
||||||
|
|
||||||
private val _autoEnabled = MutableStateFlow(true)
|
private val _autoEnabled = MutableStateFlow(false)
|
||||||
val autoEnabled: StateFlow<Boolean> = _autoEnabled.asStateFlow()
|
val autoEnabled: StateFlow<Boolean> = _autoEnabled.asStateFlow()
|
||||||
|
|
||||||
|
private val _showFirstRunDialog = MutableStateFlow(false)
|
||||||
|
val showFirstRunDialog: StateFlow<Boolean> = _showFirstRunDialog.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_autoEnabled.value = preferencesRepository.autoUpdateEnabled()
|
_autoEnabled.value = preferencesRepository.autoUpdateEnabled()
|
||||||
|
_showFirstRunDialog.value = !preferencesRepository.autoUpdateAsked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setAutoEnabled(enabled: Boolean) {
|
fun setAutoEnabled(enabled: Boolean) {
|
||||||
_autoEnabled.value = enabled
|
_autoEnabled.value = enabled
|
||||||
updater.setAutoEnabled(enabled)
|
updater.setAutoEnabled(enabled)
|
||||||
|
if (enabled && !updater.canRequestInstalls()) {
|
||||||
|
updater.openUnknownSourcesSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onFirstRunDecision(enable: Boolean) {
|
||||||
|
_showFirstRunDialog.value = false
|
||||||
|
viewModelScope.launch {
|
||||||
|
preferencesRepository.setAutoUpdateAsked(true)
|
||||||
|
}
|
||||||
|
setAutoEnabled(enable)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkNow() = updater.checkForUpdate()
|
fun checkNow() = updater.checkForUpdate()
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import android.net.Uri
|
|||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -75,6 +76,7 @@ class AppUpdater(
|
|||||||
if (job?.isActive == true) return
|
if (job?.isActive == true) return
|
||||||
val update = pendingUpdate ?: return
|
val update = pendingUpdate ?: return
|
||||||
job = scope.launch {
|
job = scope.launch {
|
||||||
|
if (!preferencesRepository.autoUpdateEnabled()) return@launch
|
||||||
runCatching { install(download(update)) }.onFailure(::onUpdateFailed)
|
runCatching { install(download(update)) }.onFailure(::onUpdateFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +87,12 @@ class AppUpdater(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setAutoEnabled(enabled: Boolean) {
|
fun setAutoEnabled(enabled: Boolean) {
|
||||||
|
if (!enabled) {
|
||||||
|
job?.cancel()
|
||||||
|
if (_state.value is UpdateState.Downloading || _state.value is UpdateState.Checking) {
|
||||||
|
_state.value = UpdateState.Idle
|
||||||
|
}
|
||||||
|
}
|
||||||
scope.launch {
|
scope.launch {
|
||||||
preferencesRepository.setAutoUpdateEnabled(enabled)
|
preferencesRepository.setAutoUpdateEnabled(enabled)
|
||||||
}
|
}
|
||||||
@@ -166,7 +174,6 @@ class AppUpdater(
|
|||||||
|
|
||||||
private fun install(apk: File) {
|
private fun install(apk: File) {
|
||||||
if (!canRequestInstalls()) {
|
if (!canRequestInstalls()) {
|
||||||
openUnknownSourcesSettings()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val uri = FileProvider.getUriForFile(
|
val uri = FileProvider.getUriForFile(
|
||||||
@@ -184,6 +191,7 @@ class AppUpdater(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onUpdateFailed(error: Throwable) {
|
private fun onUpdateFailed(error: Throwable) {
|
||||||
|
if (error is CancellationException) throw error
|
||||||
Log.e(TAG, "Ошибка обновления", error)
|
Log.e(TAG, "Ошибка обновления", error)
|
||||||
_state.value = UpdateState.Failed(error.message ?: "Неизвестная ошибка")
|
_state.value = UpdateState.Failed(error.message ?: "Неизвестная ошибка")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user