Использования менеджера Tasks

This commit is contained in:
2026-04-27 00:54:37 +03:00
parent 404ff201c4
commit 2b1be58a8e
2 changed files with 169 additions and 90 deletions

View File

@@ -23,6 +23,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.UUID
import javax.inject.Inject import javax.inject.Inject
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@@ -92,8 +93,12 @@ class LocalVaultViewModel @Inject constructor(
} }
fun printStorageInfoToLog(storage: IStorageInfo) { fun printStorageInfoToLog(storage: IStorageInfo) {
taskOrchestrator.enqueue(
title = "Dump storage to log",
dispatcher = Dispatchers.IO,
work = { ctx ->
storageFileManagementUseCase.setStorage(storage) storageFileManagementUseCase.setStorage(storage)
viewModelScope.launch { ctx.log(TaskLogLevel.Info, "Enumerating files and directories…")
val files: List<IFile> val files: List<IFile>
val dirs: List<IDirectory> val dirs: List<IDirectory>
val time = measureTimeMillis { val time = measureTimeMillis {
@@ -108,7 +113,12 @@ class LocalVaultViewModel @Inject constructor(
} }
logger.debug("Time", "Time: $time ms") logger.debug("Time", "Time: $time ms")
logger.debug("Storage", storage.toPrintable()) logger.debug("Storage", storage.toPrintable())
} ctx.log(
TaskLogLevel.Info,
"Done: ${files.size} files, ${dirs.size} dirs in ${time}ms (see app log for lines)",
)
},
)
} }
fun createStorage() { fun createStorage() {
@@ -123,71 +133,105 @@ class LocalVaultViewModel @Inject constructor(
) )
} }
private val runningStorages = mutableSetOf<java.util.UUID>() private val storageOpMutex = Any()
private val runningStorages = mutableSetOf<UUID>()
fun enableEncryption(storage: IStorageInfo, password: String, encryptPath: Boolean) { fun enableEncryption(storage: IStorageInfo, password: String, encryptPath: Boolean) {
val id = storage.uuid val id = storage.uuid
if (runningStorages.contains(id)) synchronized(storageOpMutex) {
return if (runningStorages.contains(id)) return
tasksCount++
runningStorages.add(id) runningStorages.add(id)
tasksCount++
}
val key = EncryptKey(password) val key = EncryptKey(password)
viewModelScope.launch { taskOrchestrator.enqueue(
title = "Enable encryption",
dispatcher = Dispatchers.IO,
work = { ctx ->
try { try {
ctx.log(TaskLogLevel.Info, "Checking storage…")
when (manageStoragesEncryptionUseCase.canEncrypt(storage)) { when (manageStoragesEncryptionUseCase.canEncrypt(storage)) {
ManageStoragesEncryptionUseCase.CanEncryptResult.Allowed -> { ManageStoragesEncryptionUseCase.CanEncryptResult.Allowed -> {
ctx.log(TaskLogLevel.Info, "Encrypting…")
manageStoragesEncryptionUseCase.enableEncryption(storage, key, encryptPath) manageStoragesEncryptionUseCase.enableEncryption(storage, key, encryptPath)
manageStoragesEncryptionUseCase.openStorage(storage, key, true) manageStoragesEncryptionUseCase.openStorage(storage, key, true)
ctx.log(TaskLogLevel.Info, "Encryption enabled")
_messages.emit("Encryption enabled") _messages.emit("Encryption enabled")
} }
ManageStoragesEncryptionUseCase.CanEncryptResult.AlreadyEncrypted -> { ManageStoragesEncryptionUseCase.CanEncryptResult.AlreadyEncrypted -> {
ctx.log(TaskLogLevel.Info, "Storage is already encrypted")
_messages.emit("Storage is already encrypted") _messages.emit("Storage is already encrypted")
} }
ManageStoragesEncryptionUseCase.CanEncryptResult.StorageIsNotEmpty -> { ManageStoragesEncryptionUseCase.CanEncryptResult.StorageIsNotEmpty -> {
ctx.log(TaskLogLevel.Info, "Storage is not empty")
_messages.emit("Storage is not empty") _messages.emit("Storage is not empty")
} }
ManageStoragesEncryptionUseCase.CanEncryptResult.StorageStateUnknown -> { ManageStoragesEncryptionUseCase.CanEncryptResult.StorageStateUnknown -> {
ctx.log(TaskLogLevel.Info, "Cannot determine whether storage is empty")
_messages.emit("Cannot determine whether storage is empty") _messages.emit("Cannot determine whether storage is empty")
} }
ManageStoragesEncryptionUseCase.CanEncryptResult.UnsupportedStorageType -> { ManageStoragesEncryptionUseCase.CanEncryptResult.UnsupportedStorageType -> {
ctx.log(TaskLogLevel.Info, "Unsupported storage type")
_messages.emit("Unsupported storage type") _messages.emit("Unsupported storage type")
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
ctx.log(TaskLogLevel.Error, e.message ?: "Failed to enable encryption")
_messages.emit(e.message ?: "Failed to enable encryption") _messages.emit(e.message ?: "Failed to enable encryption")
} } finally {
finally { synchronized(storageOpMutex) {
runningStorages.remove(id) runningStorages.remove(id)
tasksCount-- tasksCount--
} }
} }
},
)
} }
fun openEncryptedStorage(storage: IStorageInfo, password: String, rememberPassword: Boolean) { fun openEncryptedStorage(storage: IStorageInfo, password: String, rememberPassword: Boolean) {
val id = storage.uuid val id = storage.uuid
synchronized(storageOpMutex) {
if (runningStorages.contains(id)) return if (runningStorages.contains(id)) return
tasksCount++
runningStorages.add(id) runningStorages.add(id)
tasksCount++
}
val key = EncryptKey(password) val key = EncryptKey(password)
viewModelScope.launch { taskOrchestrator.enqueue(
title = "Open encrypted storage",
dispatcher = Dispatchers.IO,
work = { ctx ->
try { try {
ctx.log(TaskLogLevel.Info, "Opening storage…")
manageStoragesEncryptionUseCase.openStorage(storage, key, rememberPassword) manageStoragesEncryptionUseCase.openStorage(storage, key, rememberPassword)
ctx.log(TaskLogLevel.Info, "Storage opened")
} catch (e: Exception) { } catch (e: Exception) {
ctx.log(TaskLogLevel.Error, e.message ?: "Failed to open encrypted storage")
_messages.emit(e.message ?: "Failed to open encrypted storage") _messages.emit(e.message ?: "Failed to open encrypted storage")
} finally { } finally {
synchronized(storageOpMutex) {
runningStorages.remove(id) runningStorages.remove(id)
tasksCount-- tasksCount--
} }
} }
},
)
} }
fun closeEncryptedStorage(storage: IStorageInfo) { fun closeEncryptedStorage(storage: IStorageInfo) {
viewModelScope.launch { taskOrchestrator.enqueue(
title = "Close encrypted storage",
dispatcher = Dispatchers.IO,
work = { ctx ->
try { try {
ctx.log(TaskLogLevel.Info, "Closing storage…")
manageStoragesEncryptionUseCase.closeStorage(storage) manageStoragesEncryptionUseCase.closeStorage(storage)
ctx.log(TaskLogLevel.Info, "Storage closed")
} catch (e: Exception) { } catch (e: Exception) {
ctx.log(TaskLogLevel.Error, e.message ?: "Failed to close encrypted storage")
_messages.emit(e.message ?: "Failed to close encrypted storage") _messages.emit(e.message ?: "Failed to close encrypted storage")
} }
} },
)
} }
fun disableEncryption(storage: IStorageInfo) { fun disableEncryption(storage: IStorageInfo) {
@@ -211,9 +255,19 @@ class LocalVaultViewModel @Inject constructor(
} }
fun rename(storage: IStorageInfo, newName: String) { fun rename(storage: IStorageInfo, newName: String) {
viewModelScope.launch { taskOrchestrator.enqueue(
title = "Rename storage",
dispatcher = Dispatchers.IO,
work = { ctx ->
try {
ctx.log(TaskLogLevel.Info, "Renaming…")
renameStorageUseCase.rename(storage, newName) renameStorageUseCase.rename(storage, newName)
ctx.log(TaskLogLevel.Info, "Renamed")
} catch (e: Exception) {
ctx.log(TaskLogLevel.Error, e.message ?: "Rename failed")
} }
},
)
} }
fun remove(storage: IStorageInfo) { fun remove(storage: IStorageInfo) {

View File

@@ -4,18 +4,22 @@ import androidx.lifecycle.viewModelScope
import com.github.nullptroma.wallenc.domain.auth.RemoteYandexSignInLauncher import com.github.nullptroma.wallenc.domain.auth.RemoteYandexSignInLauncher
import com.github.nullptroma.wallenc.domain.interfaces.IYandexVault import com.github.nullptroma.wallenc.domain.interfaces.IYandexVault
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import com.github.nullptroma.wallenc.domain.tasks.ITaskOrchestrator
import com.github.nullptroma.wallenc.domain.tasks.TaskLogLevel
import com.github.nullptroma.wallenc.presentation.ViewModelBase import com.github.nullptroma.wallenc.presentation.ViewModelBase
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.withContext
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class RemoteVaultsViewModel @Inject constructor( class RemoteVaultsViewModel @Inject constructor(
private val vaultsManager: IVaultsManager, private val vaultsManager: IVaultsManager,
val yandexSignIn: RemoteYandexSignInLauncher, val yandexSignIn: RemoteYandexSignInLauncher,
private val taskOrchestrator: ITaskOrchestrator,
) : ViewModelBase<RemoteVaultsScreenState>(RemoteVaultsScreenState()) { ) : ViewModelBase<RemoteVaultsScreenState>(RemoteVaultsScreenState()) {
val uiState = combine( val uiState = combine(
@@ -50,15 +54,25 @@ class RemoteVaultsViewModel @Inject constructor(
} }
fun onYandexAuthSuccess(accessToken: String) { fun onYandexAuthSuccess(accessToken: String) {
viewModelScope.launch {
setBusy(true) setBusy(true)
taskOrchestrator.enqueue(
title = "Add Yandex vault",
dispatcher = Dispatchers.IO,
work = { ctx ->
try { try {
ctx.log(TaskLogLevel.Info, "Adding vault…")
vaultsManager.addYandexVault(accessToken) vaultsManager.addYandexVault(accessToken)
ctx.log(TaskLogLevel.Info, "Vault added")
} catch (e: Exception) {
ctx.log(TaskLogLevel.Error, e.message ?: "Failed to add vault")
} finally { } finally {
withContext(Dispatchers.Main.immediate) {
setBusy(false) setBusy(false)
setAddChoiceVisible(false) setAddChoiceVisible(false)
} }
} }
},
)
} }
fun requestDeleteVault(item: RemoteVaultListItem) { fun requestDeleteVault(item: RemoteVaultListItem) {
@@ -71,14 +85,25 @@ class RemoteVaultsViewModel @Inject constructor(
fun confirmDeleteVault() { fun confirmDeleteVault() {
val pending = state.value.vaultPendingDelete ?: return val pending = state.value.vaultPendingDelete ?: return
viewModelScope.launch { val uuid = pending.uuid
setBusy(true) setBusy(true)
taskOrchestrator.enqueue(
title = "Remove remote vault",
dispatcher = Dispatchers.IO,
work = { ctx ->
try { try {
vaultsManager.removeRemoteVault(pending.uuid) ctx.log(TaskLogLevel.Info, "Removing remote vault")
vaultsManager.removeRemoteVault(uuid)
ctx.log(TaskLogLevel.Info, "Remote vault removed")
} catch (e: Exception) {
ctx.log(TaskLogLevel.Error, e.message ?: "Failed to remove vault")
} finally { } finally {
withContext(Dispatchers.Main.immediate) {
setBusy(false) setBusy(false)
dismissDeleteVault() dismissDeleteVault()
} }
} }
},
)
} }
} }