Отображение хранилищ как деревья

This commit is contained in:
Пытков Роман
2025-01-27 22:19:44 +03:00
parent 851fb6bd88
commit eba0d0e3cb
9 changed files with 129 additions and 32 deletions

View File

@@ -6,7 +6,7 @@ plugins {
android {
namespace = "com.github.nullptroma.wallenc.data"
compileSdk = 34
compileSdk = 35
defaultConfig {
minSdk = 26

View File

@@ -28,4 +28,7 @@ interface StorageMetaInfoDao {
@Delete
suspend fun delete(metaInfo: DbStorageMetaInfo)
@Query("DELETE FROM storage_meta_infos WHERE uuid == :uuid")
suspend fun delete(uuid: UUID)
}

View File

@@ -3,6 +3,7 @@ package com.github.nullptroma.wallenc.data.db.app.repository
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.github.nullptroma.wallenc.data.db.app.dao.StorageMetaInfoDao
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageMetaInfo
import com.github.nullptroma.wallenc.data.utils.IProvider
import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -34,6 +35,11 @@ class StorageMetaInfoRepository(
dao.add(DbStorageMetaInfo(uuid, json))
}
suspend fun delete(uuid: UUID) = withContext(ioDispatcher) {
dao.delete(uuid)
}
fun createSingleStorageProvider(uuid: UUID): SingleStorageMetaInfoProvider {
return SingleStorageMetaInfoProvider(this, uuid)
}
@@ -41,9 +47,10 @@ class StorageMetaInfoRepository(
class SingleStorageMetaInfoProvider (
private val repo: StorageMetaInfoRepository,
val uuid: UUID
) {
suspend fun get(): CommonStorageMetaInfo? = repo.getMeta(uuid)
suspend fun set(metaInfo: CommonStorageMetaInfo) = repo.setMeta(uuid, metaInfo)
) : IProvider<CommonStorageMetaInfo> {
override suspend fun get(): CommonStorageMetaInfo? = repo.getMeta(uuid)
override suspend fun clear() = repo.delete(uuid)
override suspend fun set(value: CommonStorageMetaInfo) = repo.setMeta(uuid, value)
}
companion object {

View File

@@ -0,0 +1,7 @@
package com.github.nullptroma.wallenc.data.utils
interface IProvider<T> {
suspend fun get(): T?
suspend fun set(value: T)
suspend fun clear()
}

View File

@@ -0,0 +1,3 @@
package com.github.nullptroma.wallenc.domain.datatypes
class Tree<T>(val value: T, var children: List<Tree<T>>? = null)

View File

@@ -1,32 +1,60 @@
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CardElevation
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.github.nullptroma.wallenc.domain.datatypes.Tree
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
import com.github.nullptroma.wallenc.presentation.R
import kotlin.random.Random
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LocalVaultScreen(modifier: Modifier = Modifier,
viewModel: LocalVaultViewModel = hiltViewModel()) {
fun LocalVaultScreen(
modifier: Modifier = Modifier,
viewModel: LocalVaultViewModel = hiltViewModel()
) {
val uiState by viewModel.state.collectAsStateWithLifecycle()
Scaffold(modifier = modifier, contentWindowInsets = WindowInsets(0.dp), floatingActionButton = {
@@ -39,27 +67,63 @@ fun LocalVaultScreen(modifier: Modifier = Modifier,
}
}) { innerPadding ->
LazyColumn(modifier = Modifier.padding(innerPadding)) {
items(uiState.storagesList) {
Card(modifier = Modifier.clickable { }.pointerInput(Unit) {
detectTapGestures(
onTap = { _ -> viewModel.printStorageInfoToLog(it) }
)
}.padding(8.dp)) {
val available by it.isAvailable.collectAsStateWithLifecycle()
val numOfFiles by it.numberOfFiles.collectAsStateWithLifecycle()
val size by it.size.collectAsStateWithLifecycle()
val metaInfo by it.metaInfo.collectAsStateWithLifecycle()
val enc = metaInfo.encInfo
Column(modifier = Modifier.padding(4.dp)) {
Text(it.uuid.toString())
Text("IsAvailable: $available")
Text("Files: $numOfFiles")
Text("Size: $size")
Text("Enc: $enc")
}
}
items(uiState.storagesList) { tree ->
Storage(Modifier.padding(8.dp), tree)
}
}
}
}
}
@Composable
fun Storage(modifier: Modifier, tree: Tree<IStorageInfo>) {
val cur = tree.value
val cardShape = RoundedCornerShape(30.dp)
Column(modifier) {
Card(
modifier = Modifier
.fillMaxWidth()
.clip(cardShape)
.clickable {
//viewModel.printStorageInfoToLog(cur)
},
shape = cardShape,
elevation = CardDefaults.cardElevation(
defaultElevation = 4.dp
),
) {
val available by cur.isAvailable.collectAsStateWithLifecycle()
val numOfFiles by cur.numberOfFiles.collectAsStateWithLifecycle()
val size by cur.size.collectAsStateWithLifecycle()
val metaInfo by cur.metaInfo.collectAsStateWithLifecycle()
Row {
Column(modifier = Modifier.padding(8.dp)) {
Text(metaInfo.name ?: stringResource(R.string.no_name))
Text(
text = "IsAvailable: $available"
)
Text("Files: $numOfFiles")
Text("Size: $size")
Text("IsVirtual: ${cur.isVirtualStorage}")
Text(
modifier = Modifier
.fillMaxWidth()
.padding(0.dp,0.dp,8.dp,0.dp)
.align(Alignment.End),
text = cur.uuid.toString(),
textAlign = TextAlign.End,
fontSize = 8.sp,
style = LocalTextStyle.current.copy(
platformStyle = PlatformTextStyle(
includeFontPadding = true
)
)
)
}
}
}
for(i in tree.children ?: listOf()) {
Storage(Modifier.padding(16.dp,0.dp,0.dp,0.dp), i)
}
}
}

View File

@@ -1,5 +1,6 @@
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
import com.github.nullptroma.wallenc.domain.datatypes.Tree
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
data class LocalVaultScreenState(val storagesList: List<IStorageInfo>)
data class LocalVaultScreenState(val storagesList: List<Tree<IStorageInfo>>)

View File

@@ -2,6 +2,7 @@ package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.va
import androidx.lifecycle.viewModelScope
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
import com.github.nullptroma.wallenc.domain.datatypes.Tree
import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
import com.github.nullptroma.wallenc.domain.interfaces.IFile
import com.github.nullptroma.wallenc.domain.interfaces.ILogger
@@ -24,12 +25,22 @@ class LocalVaultViewModel @Inject constructor(
private val getOpenedStoragesUseCase: GetOpenedStoragesUseCase,
private val storageFileManagementUseCase: StorageFileManagementUseCase,
private val logger: ILogger
) :
ViewModelBase<LocalVaultScreenState>(LocalVaultScreenState(listOf())) {
) : ViewModelBase<LocalVaultScreenState>(LocalVaultScreenState(listOf())) {
init {
viewModelScope.launch {
manageLocalVaultUseCase.localStorages.combine(getOpenedStoragesUseCase.openedStorages) { local, opened ->
local + (opened?.map { it.value } ?: listOf())
val list = mutableListOf<Tree<IStorageInfo>>()
for (storage in local) {
var tree = Tree(storage)
list.add(tree)
while(opened != null && opened.containsKey(tree.value.uuid)) {
val child = opened.getValue(tree.value.uuid)
val nextTree = Tree(child)
tree.children = listOf(nextTree)
tree = nextTree
}
}
return@combine list
}.collectLatest {
val newState = state.value.copy(
storagesList = it
@@ -61,7 +72,7 @@ class LocalVaultViewModel @Inject constructor(
fun createStorage() {
viewModelScope.launch {
manageLocalVaultUseCase.createStorage(EncryptKey("hello"))
manageLocalVaultUseCase.createStorage()
}
}
}

View File

@@ -6,4 +6,5 @@
<string name="nav_label_settings">Settings</string>
<string name="settings_title">Settings Screen Title!</string>
<string name="no_name">&lt;noname&gt;</string>
</resources>