diff --git a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorage.kt b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorage.kt index 6e2fcb2..e970822 100644 --- a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorage.kt +++ b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorage.kt @@ -3,6 +3,7 @@ package com.github.nullptroma.wallenc.data.vaults.local import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo import com.github.nullptroma.wallenc.domain.interfaces.IStorage +import com.github.nullptroma.wallenc.domain.interfaces.IStorageAccessor import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -20,7 +21,8 @@ class LocalStorage( get() = accessor.numberOfFiles override val isAvailable: StateFlow get() = accessor.isAvailable - override val accessor = LocalStorageAccessor(absolutePath, ioDispatcher) + private val _accessor = LocalStorageAccessor(absolutePath, ioDispatcher) + override val accessor: IStorageAccessor = _accessor private val _encInfo = MutableStateFlow(null) override val encInfo: StateFlow @@ -31,14 +33,12 @@ class LocalStorage( private val encInfoFileName: String = "$uuid$ENC_INFO_FILE_POSTFIX" suspend fun init() { - accessor.init() + _accessor.init() readEncInfo() } private suspend fun readEncInfo() { - accessor.touchFile(encInfoFileName) - accessor.setHidden(encInfoFileName, true) - val reader = accessor.openRead(encInfoFileName) + val reader = _accessor.openReadSystemFile(encInfoFileName) var enc: StorageEncryptionInfo? = null try { enc = _jackson.readValue(reader, StorageEncryptionInfo::class.java) @@ -51,23 +51,13 @@ class LocalStorage( isEncrypted = false, encryptedTestData = null ) - val writer = accessor.openWrite(encInfoFileName) - try { - _jackson.writeValue(writer, enc) - } - catch (e: Exception) { - TODO("Это никогда не должно произойти") - } - writer.close() + setEncInfo(enc) } _encInfo.value = enc } suspend fun setEncInfo(enc: StorageEncryptionInfo) { - accessor.touchFile(encInfoFileName) - accessor.setHidden(encInfoFileName, true) - - val writer = accessor.openWrite(encInfoFileName) + val writer = _accessor.openWriteSystemFile(encInfoFileName) try { _jackson.writeValue(writer, enc) } diff --git a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt index 928df63..faa12d4 100644 --- a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt +++ b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt @@ -85,9 +85,11 @@ class LocalStorageAccessor( val children = dir.listFiles() if (children != null) { + // вызвать коллбек для каждого элемента директории for (child in children) { - callback(child) + if(child.name != SYSTEM_HIDDEN_DIRNAME) + callback(child) } if (useCallbackForSelf) @@ -96,7 +98,7 @@ class LocalStorageAccessor( if (maxDepth != 0) { val nextMaxDepth = if (maxDepth > 0) maxDepth - 1 else maxDepth for (child in children) { - if (child.isDirectory) { + if (child.isDirectory && child.name != SYSTEM_HIDDEN_DIRNAME) { scanFileSystem(child, nextMaxDepth, callback, false) } } @@ -488,7 +490,33 @@ class LocalStorageAccessor( writeMeta(pair.metaFile, newMeta) } + suspend fun openReadSystemFile(name: String): InputStream = withContext(ioDispatcher) { + val dirPath = _filesystemBasePath.resolve(SYSTEM_HIDDEN_DIRNAME) + val path = dirPath.resolve(name) + val file = path.toFile() + if(!file.exists()) { + Files.createDirectories(dirPath) + file.createNewFile() + } + + return@withContext file.inputStream() + } + + suspend fun openWriteSystemFile(name: String): OutputStream = withContext(ioDispatcher) { + val dirPath = _filesystemBasePath.resolve(SYSTEM_HIDDEN_DIRNAME) + val path = dirPath.resolve(name) + val file = path.toFile() + if(!file.exists()) { + Files.createDirectories(dirPath) + file.createNewFile() + } + + return@withContext file.outputStream() + } + companion object { + // Файлы, которые можно использовать для чтения и записи, но не отображаются в хранилище + private const val SYSTEM_HIDDEN_DIRNAME = "wallenc-local-storage-meta-dir" private const val META_INFO_POSTFIX = ".wallenc-meta" private const val DATA_PAGE_LENGTH = 10 private val _jackson = jacksonObjectMapper().apply { findAndRegisterModules() } diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt index 4e816bf..592d0d2 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt @@ -52,7 +52,7 @@ class EncryptedStorageAccessor( private val _dirsUpdates = MutableSharedFlow>>() override val dirsUpdates: SharedFlow>> = _dirsUpdates - private val _secretKey = SecretKeySpec(key.to32Bytes(), "AES") + private var _secretKey: SecretKeySpec? = SecretKeySpec(key.to32Bytes(), "AES") init { collectSourceState() @@ -123,6 +123,8 @@ class EncryptedStorageAccessor( @OptIn(ExperimentalEncodingApi::class) private fun encryptString(str: String): String { + if(_secretKey == null) + throw Exception("Object was disposed") val cipher = Cipher.getInstance(AES_SETTINGS) val iv = IvParameterSpec(Random.nextBytes(IV_LEN)) cipher.init(Cipher.ENCRYPT_MODE, _secretKey, iv) @@ -133,6 +135,8 @@ class EncryptedStorageAccessor( @OptIn(ExperimentalEncodingApi::class) private fun decryptString(str: String): String { + if(_secretKey == null) + throw Exception("Object was disposed") val cipher = Cipher.getInstance(AES_SETTINGS) val bytesToDecrypt = Base64.Default.decode(str.replace(".", "/")) val iv = IvParameterSpec(bytesToDecrypt.take(IV_LEN).toByteArray()) @@ -226,6 +230,8 @@ class EncryptedStorageAccessor( } override suspend fun openWrite(path: String): OutputStream { + if(_secretKey == null) + throw Exception("Object was disposed") val stream = source.openWrite(encryptPath(path)) val iv = IvParameterSpec(Random.nextBytes(IV_LEN)) stream.write(iv.iv) // Запись инициализационного вектора сырой файл @@ -235,6 +241,8 @@ class EncryptedStorageAccessor( } override suspend fun openRead(path: String): InputStream { + if(_secretKey == null) + throw Exception("Object was disposed") val stream = source.openRead(encryptPath(path)) val ivBytes = ByteArray(IV_LEN) // Буфер для 16 байт IV val bytesRead = stream.read(ivBytes) // Чтение IV вектора @@ -253,7 +261,7 @@ class EncryptedStorageAccessor( override fun dispose() { _job.cancel() - // TODO сделать удаление ключа, чтобы нельзя было вызвать ни один из методов + _secretKey = null } diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts index 55265ba..8387a3c 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -47,9 +47,6 @@ android { } dependencies { - // Timber - implementation(libs.timber) - implementation(libs.navigation) implementation(libs.navigation.hilt.compose) diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/extensions/StorageInfo.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/extensions/StorageInfo.kt new file mode 100644 index 0000000..3bdc329 --- /dev/null +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/extensions/StorageInfo.kt @@ -0,0 +1,7 @@ +package com.github.nullptroma.wallenc.presentation.extensions + +import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo + +fun IStorageInfo.toPrintable(): String { + return "{ uuid: $uuid, enc: ${encInfo.value} }" +} \ No newline at end of file diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt index 68e9350..74b02da 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt @@ -1,5 +1,6 @@ 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.WindowInsets @@ -39,19 +40,21 @@ fun LocalVaultScreen(modifier: Modifier = Modifier, }) { innerPadding -> LazyColumn(modifier = Modifier.padding(innerPadding)) { items(uiState.storagesList) { - Card(modifier = Modifier.pointerInput(Unit) { + Card(modifier = Modifier.clickable { }.pointerInput(Unit) { detectTapGestures( - onTap = { _ -> viewModel.printAllFilesToLog(it) } + onTap = { _ -> viewModel.printStorageInfoToLog(it) } ) }) { val available by it.isAvailable.collectAsStateWithLifecycle() val numOfFiles by it.numberOfFiles.collectAsStateWithLifecycle() val size by it.size.collectAsStateWithLifecycle() + val enc by it.encInfo.collectAsStateWithLifecycle() Column { Text(it.uuid.toString()) Text("IsAvailable: $available") Text("Files: $numOfFiles") Text("Size: $size") + Text("Enc: $enc") } } } diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt index 7a3d8fe..cd21e50 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt @@ -3,13 +3,14 @@ package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.va import androidx.lifecycle.viewModelScope 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.IStorageInfo import com.github.nullptroma.wallenc.domain.usecases.ManageLocalVaultUseCase import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase +import com.github.nullptroma.wallenc.presentation.extensions.toPrintable import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch -import timber.log.Timber import javax.inject.Inject import kotlin.system.measureTimeMillis @@ -17,6 +18,7 @@ import kotlin.system.measureTimeMillis class LocalVaultViewModel @Inject constructor( private val _manageLocalVaultUseCase: ManageLocalVaultUseCase, private val _storageFileManagementUseCase: StorageFileManagementUseCase, + private val logger: ILogger ) : ViewModelBase(LocalVaultScreenState(listOf())) { init { @@ -30,7 +32,7 @@ class LocalVaultViewModel @Inject constructor( } } - fun printAllFilesToLog(storage: IStorageInfo) { + fun printStorageInfoToLog(storage: IStorageInfo) { _storageFileManagementUseCase.setStorage(storage) viewModelScope.launch { val files: List @@ -40,14 +42,13 @@ class LocalVaultViewModel @Inject constructor( dirs = _storageFileManagementUseCase.getAllDirs() } for (file in files) { - Timber.tag("Files") - Timber.d(file.metaInfo.toString()) + logger.debug("Files", file.metaInfo.toString()) } for (dir in dirs) { - Timber.tag("Dirs") - Timber.d(dir.metaInfo.toString()) + logger.debug("Dirs", dir.metaInfo.toString()) } - Timber.d("Time: $time ms") + logger.debug("Time", "Time: $time ms") + logger.debug("Storage", storage.toPrintable()) } } diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/viewmodel/ViewModelBase.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/viewmodel/ViewModelBase.kt index e2c35e8..c92ff4d 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/viewmodel/ViewModelBase.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/viewmodel/ViewModelBase.kt @@ -4,15 +4,10 @@ package com.github.nullptroma.wallenc.presentation.viewmodel import androidx.lifecycle.ViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import timber.log.Timber abstract class ViewModelBase(initState: TState) : ViewModel() { private val _state = MutableStateFlow(initState) - init { - Timber.d("Init ViewModel ${this.javaClass.name}") - } - val state: StateFlow get() = _state