Убран лишний отступ на NavBar на основном экране
This commit is contained in:
@@ -0,0 +1,176 @@
|
|||||||
|
package com.github.nullptroma.wallenc.data.vaults
|
||||||
|
|
||||||
|
import com.github.nullptroma.wallenc.data.db.app.repository.StorageKeyMapRepository
|
||||||
|
import com.github.nullptroma.wallenc.data.model.StorageKeyMap
|
||||||
|
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||||
|
import com.github.nullptroma.wallenc.data.storages.encrypt.EncryptedStorage
|
||||||
|
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||||
|
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor
|
||||||
|
import com.github.nullptroma.wallenc.domain.enums.VaultType
|
||||||
|
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||||
|
import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager
|
||||||
|
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
class UnlockManager(
|
||||||
|
private val keymapRepository: StorageKeyMapRepository,
|
||||||
|
private val ioDispatcher: CoroutineDispatcher,
|
||||||
|
vaultsManager: IVaultsManager
|
||||||
|
) : IUnlockManager {
|
||||||
|
private val _openedStorages = MutableStateFlow<Map<UUID, EncryptedStorage>?>(null)
|
||||||
|
override val openedStorages: StateFlow<Map<UUID, IStorage>?>
|
||||||
|
get() = _openedStorages
|
||||||
|
private val mutex = Mutex()
|
||||||
|
override val type: VaultType
|
||||||
|
get() = VaultType.DECRYPTED
|
||||||
|
override val uuid: UUID
|
||||||
|
get() = TODO("Not yet implemented")
|
||||||
|
override val isAvailable: StateFlow<Boolean>
|
||||||
|
get() = MutableStateFlow(true)
|
||||||
|
override val totalSpace: StateFlow<Int?>
|
||||||
|
get() = MutableStateFlow(null)
|
||||||
|
override val availableSpace: StateFlow<Int?>
|
||||||
|
get() = MutableStateFlow(null)
|
||||||
|
|
||||||
|
override val storages: StateFlow<List<IStorage>?>
|
||||||
|
get() = openedStorages.map { it?.values?.toList() }.stateIn(
|
||||||
|
scope = CoroutineScope(ioDispatcher),
|
||||||
|
started = SharingStarted.WhileSubscribed(5000L),
|
||||||
|
initialValue = null
|
||||||
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
CoroutineScope(ioDispatcher).launch {
|
||||||
|
vaultsManager.allStorages.collectLatest {
|
||||||
|
mutex.lock()
|
||||||
|
val allKeys = keymapRepository.getAll()
|
||||||
|
val usedKeys = mutableListOf<StorageKeyMap>()
|
||||||
|
val keysToRemove = mutableListOf<StorageKeyMap>()
|
||||||
|
val allStorages = it.toMutableList()
|
||||||
|
val map = _openedStorages.value?.toMutableMap() ?: mutableMapOf()
|
||||||
|
while(allStorages.size > 0) {
|
||||||
|
val storage = allStorages[allStorages.size-1]
|
||||||
|
val key = allKeys.find { key -> key.sourceUuid == storage.uuid }
|
||||||
|
if(key == null) {
|
||||||
|
allStorages.removeAt(allStorages.size - 1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val encStorage = createEncryptedStorage(storage, key.key, key.destUuid)
|
||||||
|
map[storage.uuid] = encStorage
|
||||||
|
usedKeys.add(key)
|
||||||
|
allStorages.removeAt(allStorages.size - 1)
|
||||||
|
allStorages.add(encStorage)
|
||||||
|
}
|
||||||
|
catch (_: Exception) {
|
||||||
|
// ключ не подошёл
|
||||||
|
keysToRemove.add(key)
|
||||||
|
allStorages.removeAt(allStorages.size - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keymapRepository.delete(*keysToRemove.toTypedArray()) // удалить мёртвые ключи
|
||||||
|
_openedStorages.value = map.toMap()
|
||||||
|
mutex.unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun createEncryptedStorage(storage: IStorage, key: EncryptKey, uuid: UUID): EncryptedStorage {
|
||||||
|
return EncryptedStorage.create(
|
||||||
|
source = storage,
|
||||||
|
key = key,
|
||||||
|
ioDispatcher = ioDispatcher,
|
||||||
|
uuid = uuid
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun open(
|
||||||
|
storage: IStorage,
|
||||||
|
key: EncryptKey
|
||||||
|
): EncryptedStorage = withContext(ioDispatcher) {
|
||||||
|
mutex.lock()
|
||||||
|
val encInfo = storage.metaInfo.value.encInfo ?: throw Exception("EncInfo is null") // TODO
|
||||||
|
if (!Encryptor.checkKey(key, encInfo))
|
||||||
|
throw Exception("Incorrect Key")
|
||||||
|
|
||||||
|
val opened = _openedStorages.first { it != null }!!.toMutableMap()
|
||||||
|
val cur = opened[storage.uuid]
|
||||||
|
if (cur != null)
|
||||||
|
throw Exception("Storage is already open")
|
||||||
|
|
||||||
|
val keymap = StorageKeyMap(
|
||||||
|
sourceUuid = storage.uuid,
|
||||||
|
destUuid = UUID.randomUUID(),
|
||||||
|
key = key
|
||||||
|
)
|
||||||
|
val encStorage = createEncryptedStorage(storage, keymap.key, keymap.destUuid)
|
||||||
|
opened[storage.uuid] = encStorage
|
||||||
|
_openedStorages.value = opened
|
||||||
|
keymapRepository.add(keymap)
|
||||||
|
mutex.unlock()
|
||||||
|
return@withContext encStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Закрыть шифрование хранилища, закрывает рекурсивно, удаляя все ключи
|
||||||
|
* @param storage исходное хранилище, а не расшифрованное отображение
|
||||||
|
*/
|
||||||
|
override suspend fun close(storage: IStorage) {
|
||||||
|
close(storage.uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Закрыть шифрование хранилища, закрывает рекурсивно, удаляя все ключи
|
||||||
|
* @param uuid uuid исходного хранилища
|
||||||
|
*/
|
||||||
|
override suspend fun close(uuid: UUID): Unit = withContext(ioDispatcher) {
|
||||||
|
mutex.lock()
|
||||||
|
val opened = _openedStorages.first { it != null }!!
|
||||||
|
val enc = opened[uuid] ?: return@withContext
|
||||||
|
close(enc)
|
||||||
|
val model = StorageKeyMap(
|
||||||
|
sourceUuid = uuid,
|
||||||
|
destUuid = enc.uuid,
|
||||||
|
key = EncryptKey("")
|
||||||
|
)
|
||||||
|
_openedStorages.value = opened.toMutableMap().apply {
|
||||||
|
remove(uuid)
|
||||||
|
}
|
||||||
|
enc.dispose()
|
||||||
|
keymapRepository.delete(model)
|
||||||
|
mutex.unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun createStorage(): IStorage {
|
||||||
|
throw UnsupportedOperationException("Нельзя создать кошелёк на UnlockManager") // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun createStorage(enc: StorageEncryptionInfo): IStorage {
|
||||||
|
throw UnsupportedOperationException("Нельзя создать кошелёк на UnlockManager") // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Закрыть отображение
|
||||||
|
* @param storage исходное или расшифрованное хранилище
|
||||||
|
*/
|
||||||
|
override suspend fun remove(storage: IStorage) {
|
||||||
|
val opened = _openedStorages.first { it != null }!!
|
||||||
|
val source = opened.entries.firstOrNull {
|
||||||
|
it.key == storage.uuid || it.value.uuid == storage.uuid
|
||||||
|
}
|
||||||
|
if(source != null)
|
||||||
|
close(source.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.8.0"
|
agp = "8.9.1"
|
||||||
jacksonModuleKotlin = "2.18.2"
|
jacksonModuleKotlin = "2.18.2"
|
||||||
kotlin = "2.0.20"
|
kotlin = "2.0.20"
|
||||||
coreKtx = "1.15.0"
|
coreKtx = "1.15.0"
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
#Sat Sep 07 01:04:14 MSK 2024
|
#Sat Sep 07 01:04:14 MSK 2024
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ package com.github.nullptroma.wallenc.presentation
|
|||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
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.wrapContentHeight
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Menu
|
import androidx.compose.material.icons.rounded.Menu
|
||||||
import androidx.compose.material.icons.rounded.Settings
|
import androidx.compose.material.icons.rounded.Settings
|
||||||
@@ -19,6 +22,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
@@ -71,13 +75,15 @@ fun WallencNavRoot(viewModel: WallencViewModel = hiltViewModel()) {
|
|||||||
|
|
||||||
|
|
||||||
Scaffold(bottomBar = {
|
Scaffold(bottomBar = {
|
||||||
NavigationBar(modifier = Modifier.height(64.dp)) {
|
NavigationBar(modifier = Modifier.wrapContentHeight()) {
|
||||||
val navBackStackEntry by navState.navHostController.currentBackStackEntryAsState()
|
val navBackStackEntry by navState.navHostController.currentBackStackEntryAsState()
|
||||||
val currentRoute = navBackStackEntry?.destination?.route
|
val currentRoute = navBackStackEntry?.destination?.route
|
||||||
topLevelNavBarItems.forEach {
|
topLevelNavBarItems.forEach {
|
||||||
val routeClassName = it.key
|
val routeClassName = it.key
|
||||||
val navBarItemData = it.value
|
val navBarItemData = it.value
|
||||||
NavigationBarItem(icon = {
|
NavigationBarItem(
|
||||||
|
modifier = Modifier.wrapContentHeight(),
|
||||||
|
icon = {
|
||||||
if (navBarItemData.icon != null) Icon(
|
if (navBarItemData.icon != null) Icon(
|
||||||
navBarItemData.icon,
|
navBarItemData.icon,
|
||||||
contentDescription = stringResource(navBarItemData.nameStringResourceId)
|
contentDescription = stringResource(navBarItemData.nameStringResourceId)
|
||||||
@@ -92,7 +98,8 @@ fun WallencNavRoot(viewModel: WallencViewModel = hiltViewModel()) {
|
|||||||
if (currentRoute?.startsWith(routeClassName) != true) navState.changeTop(
|
if (currentRoute?.startsWith(routeClassName) != true) navState.changeTop(
|
||||||
route
|
route
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) { innerPaddings ->
|
}) { innerPaddings ->
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.github.nullptroma.wallenc.presentation.screens.main
|
|||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
@@ -17,6 +18,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
@@ -62,15 +64,14 @@ fun MainScreen(
|
|||||||
|
|
||||||
Scaffold(modifier = modifier, contentWindowInsets = WindowInsets(0.dp), bottomBar = {
|
Scaffold(modifier = modifier, contentWindowInsets = WindowInsets(0.dp), bottomBar = {
|
||||||
Column {
|
Column {
|
||||||
NavigationBar(modifier = Modifier.height(48.dp)) {
|
NavigationBar(windowInsets = WindowInsets(0), modifier = Modifier.height(48.dp)) {
|
||||||
val navBackStackEntry by navState.navHostController.currentBackStackEntryAsState()
|
val navBackStackEntry by navState.navHostController.currentBackStackEntryAsState()
|
||||||
val currentRoute = navBackStackEntry?.destination?.route
|
val currentRoute = navBackStackEntry?.destination?.route
|
||||||
topLevelNavBarItems.forEach {
|
topLevelNavBarItems.forEach {
|
||||||
val routeClassName = it.key
|
val routeClassName = it.key
|
||||||
val navBarItemData = it.value
|
val navBarItemData = it.value
|
||||||
NavigationBarItem(modifier = Modifier
|
NavigationBarItem(modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f),
|
||||||
.fillMaxHeight(),
|
|
||||||
icon = { Text(stringResource(navBarItemData.nameStringResourceId)) },
|
icon = { Text(stringResource(navBarItemData.nameStringResourceId)) },
|
||||||
selected = currentRoute?.startsWith(routeClassName) == true,
|
selected = currentRoute?.startsWith(routeClassName) == true,
|
||||||
onClick = {
|
onClick = {
|
||||||
@@ -80,7 +81,9 @@ fun MainScreen(
|
|||||||
navState.changeTop(
|
navState.changeTop(
|
||||||
route
|
route
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
|
label = null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
|
|||||||
Reference in New Issue
Block a user