Шифрование путей

This commit is contained in:
Roman Pytkov
2025-01-01 22:28:16 +03:00
parent 82412db962
commit 25947449af
36 changed files with 402 additions and 68 deletions

View File

@@ -0,0 +1,11 @@
package com.github.nullptroma.wallenc.app
import com.github.nullptroma.wallenc.domain.interfaces.ILogger
import timber.log.Timber
class Logger: ILogger {
override fun debug(tag: String, msg: String) {
Timber.tag(tag)
Timber.d(msg)
}
}

View File

@@ -0,0 +1,19 @@
package com.github.nullptroma.wallenc.app.di.modules.app
import com.github.nullptroma.wallenc.app.Logger
import com.github.nullptroma.wallenc.domain.interfaces.ILogger
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
class SingletonModule {
@Provides
@Singleton
fun provideLogger(): ILogger {
return Logger()
}
}

View File

@@ -2,9 +2,9 @@ package com.github.nullptroma.wallenc.app.di.modules.data
import android.content.Context import android.content.Context
import com.github.nullptroma.wallenc.app.di.modules.app.IoDispatcher import com.github.nullptroma.wallenc.app.di.modules.app.IoDispatcher
import com.github.nullptroma.wallenc.data.vaults.local.LocalVault
import com.github.nullptroma.wallenc.data.vaults.VaultsManager import com.github.nullptroma.wallenc.data.vaults.VaultsManager
import com.github.nullptroma.wallenc.domain.models.IVaultsManager import com.github.nullptroma.wallenc.data.vaults.local.LocalVault
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn

View File

@@ -1,7 +1,8 @@
package com.github.nullptroma.wallenc.app.di.modules.domain package com.github.nullptroma.wallenc.app.di.modules.domain
import com.github.nullptroma.wallenc.domain.models.IVaultsManager import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import com.github.nullptroma.wallenc.domain.usecases.GetAllRawStoragesUseCase import com.github.nullptroma.wallenc.domain.usecases.GetAllRawStoragesUseCase
import com.github.nullptroma.wallenc.domain.usecases.ManageLocalVaultUseCase
import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
@@ -18,6 +19,12 @@ class UseCasesModule {
return GetAllRawStoragesUseCase(vaultsManager) return GetAllRawStoragesUseCase(vaultsManager)
} }
@Provides
@Singleton
fun provideManageLocalVaultUseCase(vaultsManager: IVaultsManager): ManageLocalVaultUseCase {
return ManageLocalVaultUseCase(vaultsManager)
}
@Provides @Provides
@Singleton @Singleton
fun provideStorageFileManagementUseCase(): StorageFileManagementUseCase { fun provideStorageFileManagementUseCase(): StorageFileManagementUseCase {

View File

@@ -1,8 +1,8 @@
package com.github.nullptroma.wallenc.data.vaults package com.github.nullptroma.wallenc.data.vaults
import com.github.nullptroma.wallenc.data.vaults.local.LocalVault import com.github.nullptroma.wallenc.data.vaults.local.LocalVault
import com.github.nullptroma.wallenc.domain.models.IVault import com.github.nullptroma.wallenc.domain.interfaces.IVault
import com.github.nullptroma.wallenc.domain.models.IVaultsManager import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
class VaultsManager(override val localVault: LocalVault) : IVaultsManager { class VaultsManager(override val localVault: LocalVault) : IVaultsManager {

View File

@@ -1,7 +1,7 @@
package com.github.nullptroma.wallenc.data.vaults.local package com.github.nullptroma.wallenc.data.vaults.local
import com.github.nullptroma.wallenc.domain.models.IStorage import com.github.nullptroma.wallenc.domain.interfaces.IStorage
import com.github.nullptroma.wallenc.domain.models.IStorageAccessor import com.github.nullptroma.wallenc.domain.interfaces.IStorageAccessor
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import java.util.UUID import java.util.UUID

View File

@@ -8,9 +8,9 @@ import com.github.nullptroma.wallenc.data.vaults.local.entity.LocalFile
import com.github.nullptroma.wallenc.data.vaults.local.entity.LocalMetaInfo import com.github.nullptroma.wallenc.data.vaults.local.entity.LocalMetaInfo
import com.github.nullptroma.wallenc.domain.datatypes.DataPackage import com.github.nullptroma.wallenc.domain.datatypes.DataPackage
import com.github.nullptroma.wallenc.domain.datatypes.DataPage import com.github.nullptroma.wallenc.domain.datatypes.DataPage
import com.github.nullptroma.wallenc.domain.models.IDirectory import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
import com.github.nullptroma.wallenc.domain.models.IFile import com.github.nullptroma.wallenc.domain.interfaces.IFile
import com.github.nullptroma.wallenc.domain.models.IStorageAccessor import com.github.nullptroma.wallenc.domain.interfaces.IStorageAccessor
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -50,11 +50,11 @@ class LocalStorageAccessor(
private val _isAvailable = MutableStateFlow(false) private val _isAvailable = MutableStateFlow(false)
override val isAvailable: StateFlow<Boolean> = _isAvailable override val isAvailable: StateFlow<Boolean> = _isAvailable
private val _filesUpdates = MutableSharedFlow<DataPackage<IFile>>() private val _filesUpdates = MutableSharedFlow<DataPage<IFile>>()
override val filesUpdates: SharedFlow<DataPackage<IFile>> = _filesUpdates override val filesUpdates: SharedFlow<DataPage<IFile>> = _filesUpdates
private val _dirsUpdates = MutableSharedFlow<DataPackage<IDirectory>>() private val _dirsUpdates = MutableSharedFlow<DataPage<IDirectory>>()
override val dirsUpdates: SharedFlow<DataPackage<IDirectory>> = _dirsUpdates override val dirsUpdates: SharedFlow<DataPage<IDirectory>> = _dirsUpdates
init { init {
// запускам сканирование хранилища // запускам сканирование хранилища

View File

@@ -3,8 +3,8 @@ package com.github.nullptroma.wallenc.data.vaults.local
import android.content.Context import android.content.Context
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
import com.github.nullptroma.wallenc.domain.enums.VaultType import com.github.nullptroma.wallenc.domain.enums.VaultType
import com.github.nullptroma.wallenc.domain.models.IStorage import com.github.nullptroma.wallenc.domain.interfaces.IStorage
import com.github.nullptroma.wallenc.domain.models.IVault import com.github.nullptroma.wallenc.domain.interfaces.IVault
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow

View File

@@ -1,6 +1,6 @@
package com.github.nullptroma.wallenc.data.vaults.local.entity package com.github.nullptroma.wallenc.data.vaults.local.entity
import com.github.nullptroma.wallenc.domain.models.IDirectory import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
data class LocalDirectory( data class LocalDirectory(
override val metaInfo: LocalMetaInfo, override val metaInfo: LocalMetaInfo,

View File

@@ -1,5 +1,5 @@
package com.github.nullptroma.wallenc.data.vaults.local.entity package com.github.nullptroma.wallenc.data.vaults.local.entity
import com.github.nullptroma.wallenc.domain.models.IFile import com.github.nullptroma.wallenc.domain.interfaces.IFile
data class LocalFile(override val metaInfo: LocalMetaInfo) : IFile data class LocalFile(override val metaInfo: LocalMetaInfo) : IFile

View File

@@ -1,6 +1,6 @@
package com.github.nullptroma.wallenc.data.vaults.local.entity package com.github.nullptroma.wallenc.data.vaults.local.entity
import com.github.nullptroma.wallenc.domain.models.IMetaInfo import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo
import java.time.Instant import java.time.Instant

View File

@@ -1,6 +1,6 @@
package com.github.nullptroma.wallenc.domain.datatypes package com.github.nullptroma.wallenc.domain.datatypes
sealed class DataPackage<T>( class DataPackage<T>(
val data: T, val data: T,
val isLoading: Boolean? = false, val isLoading: Boolean? = false,
val isError: Boolean? = false val isError: Boolean? = false

View File

@@ -1,5 +1,10 @@
package com.github.nullptroma.wallenc.domain.datatypes package com.github.nullptroma.wallenc.domain.datatypes
class EncryptKey(val key: String) { import java.security.MessageDigest
class EncryptKey(val key: String) {
fun to32Bytes(): ByteArray {
val digest = MessageDigest.getInstance("SHA-256")
return digest.digest(key.toByteArray(Charsets.UTF_8))
}
} }

View File

@@ -0,0 +1,33 @@
package com.github.nullptroma.wallenc.domain.encrypt
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
import com.github.nullptroma.wallenc.domain.interfaces.ILogger
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
import com.github.nullptroma.wallenc.domain.interfaces.IStorageAccessor
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.StateFlow
import java.util.UUID
class EncryptedStorage(
source: IStorage,
key: EncryptKey,
logger: ILogger,
ioDispatcher: CoroutineDispatcher
) : IStorage {
override val size: StateFlow<Long?>
get() = TODO("Not yet implemented")
override val numberOfFiles: StateFlow<Int?>
get() = TODO("Not yet implemented")
override val uuid: UUID
get() = TODO("Not yet implemented")
override val name: StateFlow<String>
get() = TODO("Not yet implemented")
override val isAvailable: StateFlow<Boolean>
get() = TODO("Not yet implemented")
override val accessor: IStorageAccessor =
EncryptedStorageAccessor(source.accessor, key, logger, ioDispatcher)
override suspend fun rename(newName: String) {
TODO("Not yet implemented")
}
}

View File

@@ -0,0 +1,191 @@
package com.github.nullptroma.wallenc.domain.encrypt
import com.github.nullptroma.wallenc.domain.datatypes.DataPackage
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
import com.github.nullptroma.wallenc.domain.encrypt.entity.EncryptedDirectory
import com.github.nullptroma.wallenc.domain.encrypt.entity.EncryptedFile
import com.github.nullptroma.wallenc.domain.encrypt.entity.EncryptedMetaInfo
import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
import com.github.nullptroma.wallenc.domain.interfaces.IFile
import com.github.nullptroma.wallenc.domain.interfaces.ILogger
import com.github.nullptroma.wallenc.domain.interfaces.IStorageAccessor
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import java.io.InputStream
import java.io.OutputStream
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.io.path.Path
import kotlin.io.path.pathString
import kotlin.random.Random
class EncryptedStorageAccessor(
private val source: IStorageAccessor,
private val key: EncryptKey,
private val logger: ILogger,
ioDispatcher: CoroutineDispatcher
) : IStorageAccessor {
override val size: StateFlow<Long?> = source.size
override val numberOfFiles: StateFlow<Int?> = source.numberOfFiles
override val isAvailable: StateFlow<Boolean> = source.isAvailable
private val _filesUpdates = MutableSharedFlow<DataPackage<List<IFile>>>()
override val filesUpdates: SharedFlow<DataPackage<List<IFile>>> = _filesUpdates
private val _dirsUpdates = MutableSharedFlow<DataPackage<List<IDirectory>>>()
override val dirsUpdates: SharedFlow<DataPackage<List<IDirectory>>> = _dirsUpdates
private val _secretKey = SecretKeySpec(key.to32Bytes(), "AES")
init {
val enc = encryptPath("/hello/world/test.txt")
val dec = decryptPath(enc)
collectSourceState(CoroutineScope(ioDispatcher))
}
private fun collectSourceState(coroutineScope: CoroutineScope) {
coroutineScope.launch {
launch {
source.filesUpdates.collect {
val files = it.data.map {
val meta = it.metaInfo
EncryptedFile(EncryptedMetaInfo(
size = meta.size,
isDeleted = meta.isDeleted,
isHidden = meta.isHidden,
lastModified = meta.lastModified,
path = decryptPath(meta.path)
))
}
_filesUpdates.emit(DataPackage(
data = files,
isLoading = it.isLoading,
isError = it.isError
))
}
}
launch {
source.dirsUpdates.collect {
val dirs = it.data.map {
val meta = it.metaInfo
EncryptedDirectory(EncryptedMetaInfo(
size = meta.size,
isDeleted = meta.isDeleted,
isHidden = meta.isHidden,
lastModified = meta.lastModified,
path = decryptPath(meta.path)
), it.elementsCount)
}
_dirsUpdates.emit(DataPackage(
data = dirs,
isLoading = it.isLoading,
isError = it.isError
))
}
}
}
}
@OptIn(ExperimentalEncodingApi::class)
private fun encryptString(str: String): String {
val cipher = Cipher.getInstance(AES_FOR_STRINGS)
val iv = IvParameterSpec(Random.nextBytes(IV_LEN))
cipher.init(Cipher.ENCRYPT_MODE, _secretKey, iv)
val bytesToEncrypt = iv.iv + str.toByteArray(Charsets.UTF_8)
val encryptedBytes = cipher.doFinal(bytesToEncrypt)
return Base64.Default.encode(encryptedBytes).replace("/", ".")
}
@OptIn(ExperimentalEncodingApi::class)
private fun decryptString(str: String): String {
val cipher = Cipher.getInstance(AES_FOR_STRINGS)
val bytesToDecrypt = Base64.Default.decode(str.replace(".", "/"))
val iv = IvParameterSpec(bytesToDecrypt.take(IV_LEN).toByteArray())
cipher.init(Cipher.DECRYPT_MODE, _secretKey, iv)
val decryptedBytes = cipher.doFinal(bytesToDecrypt.drop(IV_LEN).toByteArray())
return String(decryptedBytes, Charsets.UTF_8)
}
private fun encryptPath(pathStr: String): String {
val path = Path(pathStr)
val segments = mutableListOf<String>()
for (segment in path)
segments.add(encryptString(segment.pathString))
val res = Path("/",*(segments.toTypedArray()))
logger.debug("encryptPath", "$pathStr to $res")
return res.pathString
}
private fun decryptPath(pathStr: String): String {
val path = Path(pathStr)
val segments = mutableListOf<String>()
for (segment in path)
segments.add(decryptString(segment.pathString))
val res = Path("/",*(segments.toTypedArray()))
logger.debug("decryptPath", "$pathStr to $res")
return res.pathString
}
override suspend fun getAllFiles(): List<IFile> {
TODO("Not yet implemented")
}
override suspend fun getFiles(path: String): List<IFile> {
TODO("Not yet implemented")
}
override fun getFilesFlow(path: String): Flow<DataPackage<List<IFile>>> {
TODO("Not yet implemented")
}
override suspend fun getAllDirs(): List<IDirectory> {
TODO("Not yet implemented")
}
override suspend fun getDirs(path: String): List<IDirectory> {
TODO("Not yet implemented")
}
override fun getDirsFlow(path: String): Flow<DataPackage<List<IDirectory>>> {
TODO("Not yet implemented")
}
override suspend fun touchFile(path: String) {
TODO("Not yet implemented")
}
override suspend fun touchDir(path: String) {
TODO("Not yet implemented")
}
override suspend fun delete(path: String) {
TODO("Not yet implemented")
}
override suspend fun openWrite(path: String): OutputStream {
TODO("Not yet implemented")
}
override suspend fun openRead(path: String): InputStream {
TODO("Not yet implemented")
}
override suspend fun moveToTrash(path: String) {
TODO("Not yet implemented")
}
companion object {
private const val IV_LEN = 16
private const val AES_FOR_STRINGS = "AES/CBC/PKCS5Padding"
}
}

View File

@@ -0,0 +1,8 @@
package com.github.nullptroma.wallenc.domain.encrypt.entity
import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
data class EncryptedDirectory(
override val metaInfo: EncryptedMetaInfo,
override val elementsCount: Int?
) : IDirectory

View File

@@ -0,0 +1,5 @@
package com.github.nullptroma.wallenc.domain.encrypt.entity
import com.github.nullptroma.wallenc.domain.interfaces.IFile
data class EncryptedFile(override val metaInfo: EncryptedMetaInfo) : IFile

View File

@@ -0,0 +1,13 @@
package com.github.nullptroma.wallenc.domain.encrypt.entity
import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo
import java.time.Instant
data class EncryptedMetaInfo(
override val size: Long,
override val isDeleted: Boolean = false,
override val isHidden: Boolean = false,
override val lastModified: Instant = java.time.Clock.systemUTC().instant(),
override val path: String
) : IMetaInfo

View File

@@ -1,4 +1,4 @@
package com.github.nullptroma.wallenc.domain.models package com.github.nullptroma.wallenc.domain.interfaces
interface IDirectory { interface IDirectory {
val metaInfo: IMetaInfo val metaInfo: IMetaInfo

View File

@@ -0,0 +1,5 @@
package com.github.nullptroma.wallenc.domain.interfaces
interface IFile {
val metaInfo: IMetaInfo
}

View File

@@ -0,0 +1,5 @@
package com.github.nullptroma.wallenc.domain.interfaces
interface ILogger {
fun debug(tag: String, msg: String)
}

View File

@@ -1,4 +1,4 @@
package com.github.nullptroma.wallenc.domain.models package com.github.nullptroma.wallenc.domain.interfaces
import java.time.Instant import java.time.Instant

View File

@@ -1,4 +1,4 @@
package com.github.nullptroma.wallenc.domain.models package com.github.nullptroma.wallenc.domain.interfaces
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import java.util.UUID import java.util.UUID

View File

@@ -1,4 +1,4 @@
package com.github.nullptroma.wallenc.domain.models package com.github.nullptroma.wallenc.domain.interfaces
import com.github.nullptroma.wallenc.domain.datatypes.DataPackage import com.github.nullptroma.wallenc.domain.datatypes.DataPackage
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -11,8 +11,8 @@ interface IStorageAccessor {
val size: StateFlow<Long?> val size: StateFlow<Long?>
val numberOfFiles: StateFlow<Int?> val numberOfFiles: StateFlow<Int?>
val isAvailable: StateFlow<Boolean> val isAvailable: StateFlow<Boolean>
val filesUpdates: SharedFlow<DataPackage<IFile>> val filesUpdates: SharedFlow<DataPackage<List<IFile>>>
val dirsUpdates: SharedFlow<DataPackage<IDirectory>> val dirsUpdates: SharedFlow<DataPackage<List<IDirectory>>>
suspend fun getAllFiles(): List<IFile> suspend fun getAllFiles(): List<IFile>
suspend fun getFiles(path: String): List<IFile> suspend fun getFiles(path: String): List<IFile>

View File

@@ -1,4 +1,4 @@
package com.github.nullptroma.wallenc.domain.models package com.github.nullptroma.wallenc.domain.interfaces
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow

View File

@@ -1,4 +1,4 @@
package com.github.nullptroma.wallenc.domain.models package com.github.nullptroma.wallenc.domain.interfaces
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
import java.util.UUID import java.util.UUID

View File

@@ -1,4 +1,4 @@
package com.github.nullptroma.wallenc.domain.models package com.github.nullptroma.wallenc.domain.interfaces
import com.github.nullptroma.wallenc.domain.enums.VaultType import com.github.nullptroma.wallenc.domain.enums.VaultType
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow

View File

@@ -1,4 +1,4 @@
package com.github.nullptroma.wallenc.domain.models package com.github.nullptroma.wallenc.domain.interfaces
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow

View File

@@ -1,5 +0,0 @@
package com.github.nullptroma.wallenc.domain.models
interface IFile {
val metaInfo: IMetaInfo
}

View File

@@ -1,6 +1,6 @@
package com.github.nullptroma.wallenc.domain.usecases package com.github.nullptroma.wallenc.domain.usecases
import com.github.nullptroma.wallenc.domain.models.IVaultsManager import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
class GetAllRawStoragesUseCase(private val manager: IVaultsManager) { class GetAllRawStoragesUseCase(private val manager: IVaultsManager) {
// fun getStoragesFlow() = manager.remoteVaults.combine(manager.localVault) { remote, local -> // fun getStoragesFlow() = manager.remoteVaults.combine(manager.localVault) { remote, local ->
@@ -9,6 +9,6 @@ class GetAllRawStoragesUseCase(private val manager: IVaultsManager) {
// add(local) // add(local)
// } // }
// } // }
val localStorage val localStorages
get() = manager.localVault get() = manager.localVault.storages
} }

View File

@@ -0,0 +1,12 @@
package com.github.nullptroma.wallenc.domain.usecases
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
class ManageLocalVaultUseCase(private val manager: IVaultsManager) {
val localStorages
get() = manager.localVault.storages
suspend fun createStorage() {
manager.localVault.createStorage()
}
}

View File

@@ -1,8 +1,8 @@
package com.github.nullptroma.wallenc.domain.usecases package com.github.nullptroma.wallenc.domain.usecases
import com.github.nullptroma.wallenc.domain.models.IDirectory import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
import com.github.nullptroma.wallenc.domain.models.IFile import com.github.nullptroma.wallenc.domain.interfaces.IFile
import com.github.nullptroma.wallenc.domain.models.IStorage import com.github.nullptroma.wallenc.domain.interfaces.IStorage
class StorageFileManagementUseCase { class StorageFileManagementUseCase {
private var _storage: IStorage? = null private var _storage: IStorage? = null

View File

@@ -1,6 +1,6 @@
package com.github.nullptroma.wallenc.domain.usecases package com.github.nullptroma.wallenc.domain.usecases
import com.github.nullptroma.wallenc.domain.models.IMetaInfo import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo
class TestUseCase (val meta: IMetaInfo, val id: Int) { class TestUseCase (val meta: IMetaInfo, val id: Int) {

View File

@@ -1,16 +1,24 @@
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
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.filled.Add
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
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
@@ -20,10 +28,21 @@ fun LocalVaultScreen(modifier: Modifier = Modifier,
viewModel: LocalVaultViewModel = hiltViewModel()) { viewModel: LocalVaultViewModel = hiltViewModel()) {
val uiState by viewModel.state.collectAsStateWithLifecycle() val uiState by viewModel.state.collectAsStateWithLifecycle()
LazyColumn(modifier = modifier) { Scaffold(modifier = modifier, contentWindowInsets = WindowInsets(0.dp), floatingActionButton = {
FloatingActionButton(
onClick = {
viewModel.createStorage()
},
) {
Icon(Icons.Filled.Add, "Floating action button.")
}
}) { innerPadding ->
LazyColumn(modifier = Modifier.padding(innerPadding)) {
items(uiState.storagesList) { items(uiState.storagesList) {
Card(modifier = Modifier.clickable { Card(modifier = Modifier.pointerInput(Unit) {
viewModel.printAllFilesToLog(it) detectTapGestures(
onTap = { _ -> viewModel.printAllFilesToLog(it) }
)
}) { }) {
val available by it.isAvailable.collectAsStateWithLifecycle() val available by it.isAvailable.collectAsStateWithLifecycle()
val numOfFiles by it.numberOfFiles.collectAsStateWithLifecycle() val numOfFiles by it.numberOfFiles.collectAsStateWithLifecycle()
@@ -38,3 +57,4 @@ fun LocalVaultScreen(modifier: Modifier = Modifier,
} }
} }
} }
}

View File

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

View File

@@ -1,12 +1,11 @@
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
import android.app.Activity
import android.widget.Toast
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.github.nullptroma.wallenc.domain.models.IDirectory import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
import com.github.nullptroma.wallenc.domain.models.IFile import com.github.nullptroma.wallenc.domain.interfaces.IFile
import com.github.nullptroma.wallenc.domain.models.IStorage import com.github.nullptroma.wallenc.domain.interfaces.ILogger
import com.github.nullptroma.wallenc.domain.usecases.GetAllRawStoragesUseCase import com.github.nullptroma.wallenc.domain.interfaces.IStorage
import com.github.nullptroma.wallenc.domain.usecases.ManageLocalVaultUseCase
import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase
import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@@ -17,13 +16,13 @@ import kotlin.system.measureTimeMillis
@HiltViewModel @HiltViewModel
class LocalVaultViewModel @Inject constructor( class LocalVaultViewModel @Inject constructor(
private val _getAllRawStoragesUseCase: GetAllRawStoragesUseCase, private val _manageLocalVaultUseCase: ManageLocalVaultUseCase,
private val _storageFileManagementUseCase: StorageFileManagementUseCase private val _storageFileManagementUseCase: StorageFileManagementUseCase,
) : ) :
ViewModelBase<LocalVaultScreenState>(LocalVaultScreenState(listOf())) { ViewModelBase<LocalVaultScreenState>(LocalVaultScreenState(listOf())) {
init { init {
viewModelScope.launch { viewModelScope.launch {
_getAllRawStoragesUseCase.localStorage.storages.collect { _manageLocalVaultUseCase.localStorages.collect {
val newState = state.value.copy( val newState = state.value.copy(
storagesList = it storagesList = it
) )
@@ -52,4 +51,10 @@ class LocalVaultViewModel @Inject constructor(
Timber.d("Time: $time ms") Timber.d("Time: $time ms")
} }
} }
fun createStorage() {
viewModelScope.launch {
_manageLocalVaultUseCase.createStorage()
}
}
} }