Поправлен клик сквозь экран загрузки
This commit is contained in:
@@ -46,7 +46,13 @@ class EncryptedStorage private constructor(
|
|||||||
override val isAvailable: StateFlow<Boolean>
|
override val isAvailable: StateFlow<Boolean>
|
||||||
get() = source.isAvailable
|
get() = source.isAvailable
|
||||||
override val accessor: EncryptedStorageAccessor =
|
override val accessor: EncryptedStorageAccessor =
|
||||||
EncryptedStorageAccessor(source.accessor, encInfo.pathIv, key, "${uuid.toString().take(8)}$SYSTEM_HIDDEN_DIRNAME_POSTFIX", scope)
|
EncryptedStorageAccessor(
|
||||||
|
source = source.accessor,
|
||||||
|
pathIv = encInfo.pathIv,
|
||||||
|
key = key,
|
||||||
|
systemHiddenDirName = "${uuid.toString().take(8)}$SYSTEM_HIDDEN_DIRNAME_POSTFIX",
|
||||||
|
scope = scope
|
||||||
|
)
|
||||||
|
|
||||||
private suspend fun init() {
|
private suspend fun init() {
|
||||||
checkKey()
|
checkKey()
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ class EncryptedStorageAccessor(
|
|||||||
private val dataEncryptor = Encryptor(key.toAesKey())
|
private val dataEncryptor = Encryptor(key.toAesKey())
|
||||||
private val pathEncryptor: EncryptorWithStaticIv? = if(pathIv != null) EncryptorWithStaticIv(key.toAesKey(), pathIv) else null
|
private val pathEncryptor: EncryptorWithStaticIv? = if(pathIv != null) EncryptorWithStaticIv(key.toAesKey(), pathIv) else null
|
||||||
|
|
||||||
private var systemHiddenFiles: List<IFile>? = null
|
|
||||||
private var systemHiddenFilesIsActual = false
|
private var systemHiddenFilesIsActual = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
interface IVault : IVaultInfo {
|
interface IVault : IVaultInfo {
|
||||||
override val storages: StateFlow<List<IStorage>>
|
override val storages: StateFlow<List<IStorage>?>
|
||||||
|
|
||||||
suspend fun createStorage(): IStorage
|
suspend fun createStorage(): IStorage
|
||||||
suspend fun createStorage(enc: StorageEncryptionInfo): IStorage
|
suspend fun createStorage(enc: StorageEncryptionInfo): IStorage
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import java.util.UUID
|
|||||||
sealed interface IVaultInfo {
|
sealed interface IVaultInfo {
|
||||||
val type: VaultType
|
val type: VaultType
|
||||||
val uuid: UUID
|
val uuid: UUID
|
||||||
val storages: StateFlow<List<IStorageInfo>>
|
val storages: StateFlow<List<IStorageInfo>?>
|
||||||
val isAvailable: StateFlow<Boolean>
|
val isAvailable: StateFlow<Boolean>
|
||||||
val totalSpace: StateFlow<Int?>
|
val totalSpace: StateFlow<Int?>
|
||||||
val availableSpace: StateFlow<Int?>
|
val availableSpace: StateFlow<Int?>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
class ManageLocalVaultUseCase(private val manager: IVaultsManager, private val unlockManager: IUnlockManager) {
|
class ManageLocalVaultUseCase(private val manager: IVaultsManager, private val unlockManager: IUnlockManager) {
|
||||||
val localStorages: StateFlow<List<IStorageInfo>>
|
val localStorages: StateFlow<List<IStorageInfo>?>
|
||||||
get() = manager.localVault.storages
|
get() = manager.localVault.storages
|
||||||
|
|
||||||
suspend fun createStorage() {
|
suspend fun createStorage() {
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.composed
|
import androidx.compose.ui.composed
|
||||||
|
import androidx.compose.ui.input.pointer.PointerEventPass
|
||||||
|
import androidx.compose.ui.input.pointer.PointerInputChange
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.layout.layout
|
import androidx.compose.ui.layout.layout
|
||||||
import androidx.compose.ui.platform.debugInspectorInfo
|
import androidx.compose.ui.platform.debugInspectorInfo
|
||||||
import androidx.compose.ui.semantics.Role
|
import androidx.compose.ui.semantics.Role
|
||||||
@@ -32,3 +35,19 @@ fun Modifier.ignoreVerticalParentPadding(vertical: Dp): Modifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Modifier.gesturesDisabled(disabled: Boolean = true) =
|
||||||
|
if (disabled) {
|
||||||
|
pointerInput(Unit) {
|
||||||
|
awaitPointerEventScope {
|
||||||
|
// we should wait for all new pointer events
|
||||||
|
while (true) {
|
||||||
|
awaitPointerEvent(pass = PointerEventPass.Initial)
|
||||||
|
.changes
|
||||||
|
.forEach(PointerInputChange::consume)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this
|
||||||
|
}
|
||||||
@@ -1,26 +1,39 @@
|
|||||||
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
|
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
|
||||||
|
|
||||||
|
import android.widget.ProgressBar
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.github.nullptroma.wallenc.presentation.elements.StorageTree
|
import com.github.nullptroma.wallenc.presentation.elements.StorageTree
|
||||||
|
import com.github.nullptroma.wallenc.presentation.extensions.gesturesDisabled
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LocalVaultScreen(
|
fun LocalVaultScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@@ -29,6 +42,7 @@ fun LocalVaultScreen(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
val uiState by viewModel.state.collectAsStateWithLifecycle()
|
val uiState by viewModel.state.collectAsStateWithLifecycle()
|
||||||
|
Box {
|
||||||
Scaffold(modifier = modifier, contentWindowInsets = WindowInsets(0.dp), floatingActionButton = {
|
Scaffold(modifier = modifier, contentWindowInsets = WindowInsets(0.dp), floatingActionButton = {
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
@@ -38,7 +52,7 @@ fun LocalVaultScreen(
|
|||||||
Icon(Icons.Filled.Add, "Floating action button.")
|
Icon(Icons.Filled.Add, "Floating action button.")
|
||||||
}
|
}
|
||||||
}) { innerPadding ->
|
}) { innerPadding ->
|
||||||
LazyColumn(modifier = Modifier.padding(innerPadding)) {
|
LazyColumn(modifier = Modifier.padding(innerPadding).gesturesDisabled(uiState.isLoading)) {
|
||||||
items(uiState.storagesList) { listItem ->
|
items(uiState.storagesList) { listItem ->
|
||||||
StorageTree(
|
StorageTree(
|
||||||
modifier = Modifier.padding(8.dp, 8.dp, 8.dp, 0.dp),
|
modifier = Modifier.padding(8.dp, 8.dp, 8.dp, 0.dp),
|
||||||
@@ -62,5 +76,17 @@ fun LocalVaultScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(uiState.isLoading) {
|
||||||
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
Box(modifier = Modifier.fillMaxSize().alpha(0.6f).background(Color.Black))
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.width(64.dp),
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
trackColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.va
|
|||||||
import com.github.nullptroma.wallenc.domain.datatypes.Tree
|
import com.github.nullptroma.wallenc.domain.datatypes.Tree
|
||||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
|
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
|
||||||
|
|
||||||
data class LocalVaultScreenState(val storagesList: List<Tree<IStorageInfo>>)
|
data class LocalVaultScreenState(val storagesList: List<Tree<IStorageInfo>>, val isLoading: Boolean)
|
||||||
@@ -29,15 +29,43 @@ class LocalVaultViewModel @Inject constructor(
|
|||||||
private val manageStoragesEncryptionUseCase: ManageStoragesEncryptionUseCase,
|
private val manageStoragesEncryptionUseCase: ManageStoragesEncryptionUseCase,
|
||||||
private val renameStorageUseCase: RenameStorageUseCase,
|
private val renameStorageUseCase: RenameStorageUseCase,
|
||||||
private val logger: ILogger
|
private val logger: ILogger
|
||||||
) : ViewModelBase<LocalVaultScreenState>(LocalVaultScreenState(listOf())) {
|
) : ViewModelBase<LocalVaultScreenState>(LocalVaultScreenState(listOf(), true)) {
|
||||||
|
private var _taskCount: Int = 0
|
||||||
|
private var tasksCount
|
||||||
|
get() = _taskCount
|
||||||
|
set(value) {
|
||||||
|
_taskCount = value
|
||||||
|
updateStateLoading()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _isLoading: Boolean = false
|
||||||
|
private var isLoading
|
||||||
|
get() = _isLoading
|
||||||
|
set(value) {
|
||||||
|
_isLoading = value
|
||||||
|
updateStateLoading()
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
collectFlows()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateStateLoading() {
|
||||||
|
updateState(state.value.copy(
|
||||||
|
isLoading = this.isLoading || this.tasksCount > 0
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectFlows() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
manageLocalVaultUseCase.localStorages.combine(getOpenedStoragesUseCase.openedStorages) { local, opened ->
|
manageLocalVaultUseCase.localStorages.combine(getOpenedStoragesUseCase.openedStorages) { local, opened ->
|
||||||
|
if(local == null || opened == null)
|
||||||
|
return@combine null
|
||||||
val list = mutableListOf<Tree<IStorageInfo>>()
|
val list = mutableListOf<Tree<IStorageInfo>>()
|
||||||
for (storage in local) {
|
for (storage in local) {
|
||||||
var tree = Tree(storage)
|
var tree = Tree(storage)
|
||||||
list.add(tree)
|
list.add(tree)
|
||||||
while(opened != null && opened.containsKey(tree.value.uuid)) {
|
while(opened.containsKey(tree.value.uuid)) {
|
||||||
val child = opened.getValue(tree.value.uuid)
|
val child = opened.getValue(tree.value.uuid)
|
||||||
val nextTree = Tree(child)
|
val nextTree = Tree(child)
|
||||||
tree.children = listOf(nextTree)
|
tree.children = listOf(nextTree)
|
||||||
@@ -46,17 +74,13 @@ class LocalVaultViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
return@combine list
|
return@combine list
|
||||||
}.collectLatest {
|
}.collectLatest {
|
||||||
|
isLoading = it == null
|
||||||
val newState = state.value.copy(
|
val newState = state.value.copy(
|
||||||
storagesList = it
|
storagesList = it ?: listOf()
|
||||||
)
|
)
|
||||||
updateState(newState)
|
updateState(newState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModelScope.launch {
|
|
||||||
getOpenedStoragesUseCase.openedStorages.collectLatest {
|
|
||||||
logger.debug("ViewModel", "Collected opened: ${it?.size}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printStorageInfoToLog(storage: IStorageInfo) {
|
fun printStorageInfoToLog(storage: IStorageInfo) {
|
||||||
@@ -80,8 +104,10 @@ class LocalVaultViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createStorage() {
|
fun createStorage() {
|
||||||
|
tasksCount++
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
manageLocalVaultUseCase.createStorage()
|
manageLocalVaultUseCase.createStorage()
|
||||||
|
tasksCount--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +115,7 @@ class LocalVaultViewModel @Inject constructor(
|
|||||||
fun enableEncryptionAndOpenStorage(storage: IStorageInfo) {
|
fun enableEncryptionAndOpenStorage(storage: IStorageInfo) {
|
||||||
if(runningStorages.contains(storage))
|
if(runningStorages.contains(storage))
|
||||||
return
|
return
|
||||||
|
tasksCount++
|
||||||
runningStorages.add(storage)
|
runningStorages.add(storage)
|
||||||
val key = EncryptKey("Hello")
|
val key = EncryptKey("Hello")
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -98,6 +125,7 @@ class LocalVaultViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
runningStorages.remove(storage)
|
runningStorages.remove(storage)
|
||||||
|
tasksCount--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user