Обновление времени при записи, StorageMetaInfo
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
package com.github.nullptroma.wallenc.data.utils
|
||||
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
private class CloseHandledOutputStream(
|
||||
private val stream: OutputStream,
|
||||
private val onClose: () -> Unit
|
||||
) : OutputStream() {
|
||||
|
||||
override fun write(b: Int) {
|
||||
stream.write(b)
|
||||
}
|
||||
|
||||
override fun write(b: ByteArray) {
|
||||
stream.write(b)
|
||||
}
|
||||
|
||||
override fun write(b: ByteArray, off: Int, len: Int) {
|
||||
stream.write(b, off, len)
|
||||
}
|
||||
|
||||
override fun flush() {
|
||||
stream.flush()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
try {
|
||||
stream.close()
|
||||
} finally {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CloseHandledInputStream(
|
||||
private val stream: InputStream,
|
||||
private val onClose: () -> Unit
|
||||
) : InputStream() {
|
||||
|
||||
override fun read(): Int {
|
||||
return stream.read()
|
||||
}
|
||||
|
||||
override fun read(b: ByteArray): Int {
|
||||
return stream.read(b)
|
||||
}
|
||||
|
||||
override fun read(b: ByteArray, off: Int, len: Int): Int {
|
||||
return stream.read(b, off, len)
|
||||
}
|
||||
|
||||
override fun skip(n: Long): Long {
|
||||
return stream.skip(n)
|
||||
}
|
||||
|
||||
override fun available(): Int {
|
||||
return stream.available()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
try {
|
||||
stream.close()
|
||||
} finally {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
override fun mark(readlimit: Int) {
|
||||
stream.mark(readlimit)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
stream.reset()
|
||||
}
|
||||
|
||||
override fun markSupported(): Boolean {
|
||||
return stream.markSupported()
|
||||
}
|
||||
}
|
||||
|
||||
class CloseHandledStreamExtension {
|
||||
companion object {
|
||||
fun OutputStream.onClose(callback: ()->Unit): OutputStream {
|
||||
return CloseHandledOutputStream(this, callback)
|
||||
}
|
||||
|
||||
fun InputStream.onClose(callback: ()->Unit): InputStream {
|
||||
return CloseHandledInputStream(this, callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class UnlockManager(
|
||||
private val _openedStorages = MutableStateFlow<Map<UUID, EncryptedStorage>?>(null)
|
||||
override val openedStorages: StateFlow<Map<UUID, IStorage>?>
|
||||
get() = _openedStorages
|
||||
val mutex = Mutex()
|
||||
private val mutex = Mutex()
|
||||
|
||||
init {
|
||||
CoroutineScope(ioDispatcher).launch {
|
||||
@@ -51,7 +51,7 @@ class UnlockManager(
|
||||
|
||||
private fun createEncryptedStorage(storage: IStorage, key: EncryptKey, uuid: UUID): EncryptedStorage {
|
||||
return EncryptedStorage(
|
||||
source = storage,
|
||||
_source = storage,
|
||||
key = key,
|
||||
ioDispatcher = ioDispatcher,
|
||||
uuid = uuid
|
||||
@@ -63,13 +63,10 @@ class UnlockManager(
|
||||
key: EncryptKey
|
||||
) = withContext(ioDispatcher) {
|
||||
mutex.lock()
|
||||
val encInfo = storage.encInfo.value ?: throw Exception("EncInfo is null") // TODO
|
||||
val encInfo = storage.metaInfo.value.encInfo ?: throw Exception("EncInfo is null") // TODO
|
||||
if (!Encryptor.checkKey(key, encInfo))
|
||||
throw Exception("Incorrect Key")
|
||||
|
||||
if (_openedStorages.value == null) {
|
||||
val childScope = CoroutineScope(ioDispatcher)
|
||||
}
|
||||
val opened = _openedStorages.first { it != null }!!.toMutableMap()
|
||||
val cur = opened[storage.uuid]
|
||||
if (cur != null)
|
||||
|
||||
@@ -1,35 +1,46 @@
|
||||
package com.github.nullptroma.wallenc.data.vaults.local
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo
|
||||
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 com.github.nullptroma.wallenc.domain.interfaces.IStorageMetaInfo
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.InputStream
|
||||
import java.util.UUID
|
||||
|
||||
|
||||
class LocalStorage(
|
||||
override val uuid: UUID,
|
||||
absolutePath: String,
|
||||
ioDispatcher: CoroutineDispatcher,
|
||||
private val ioDispatcher: CoroutineDispatcher,
|
||||
) : IStorage {
|
||||
override val size: StateFlow<Long?>
|
||||
get() = accessor.size
|
||||
override val numberOfFiles: StateFlow<Int?>
|
||||
get() = accessor.numberOfFiles
|
||||
|
||||
private val _metaInfo = MutableStateFlow(
|
||||
CommonStorageMetaInfo(
|
||||
encInfo = StorageEncryptionInfo(
|
||||
isEncrypted = false,
|
||||
encryptedTestData = null
|
||||
),
|
||||
name = null
|
||||
)
|
||||
)
|
||||
override val metaInfo: StateFlow<IStorageMetaInfo>
|
||||
get() = _metaInfo
|
||||
|
||||
override val isAvailable: StateFlow<Boolean>
|
||||
get() = accessor.isAvailable
|
||||
private val _accessor = LocalStorageAccessor(absolutePath, ioDispatcher)
|
||||
override val accessor: IStorageAccessor = _accessor
|
||||
|
||||
private val _encInfo = MutableStateFlow<StorageEncryptionInfo?>(null)
|
||||
override val encInfo: StateFlow<StorageEncryptionInfo?>
|
||||
get() = _encInfo
|
||||
override val name: StateFlow<String>
|
||||
get() = TODO("Добавить класс в Domain, который с помощью accessor будет читать и сохранять имя в скрытую папку")
|
||||
|
||||
private val encInfoFileName: String = "$uuid$ENC_INFO_FILE_POSTFIX"
|
||||
|
||||
suspend fun init() {
|
||||
@@ -37,15 +48,14 @@ class LocalStorage(
|
||||
readEncInfo()
|
||||
}
|
||||
|
||||
private suspend fun readEncInfo() {
|
||||
val reader = _accessor.openReadSystemFile(encInfoFileName)
|
||||
private suspend fun readEncInfo() = withContext(ioDispatcher) {
|
||||
var enc: StorageEncryptionInfo? = null
|
||||
var reader: InputStream? = null
|
||||
try {
|
||||
enc = _jackson.readValue(reader, StorageEncryptionInfo::class.java)
|
||||
reader.close()
|
||||
reader = _accessor.openReadSystemFile(encInfoFileName)
|
||||
enc = jackson.readValue(reader, StorageEncryptionInfo::class.java)
|
||||
}
|
||||
catch(e: Exception) {
|
||||
reader.close()
|
||||
// чтение не удалось, значит нужно записать файл
|
||||
enc = StorageEncryptionInfo(
|
||||
isEncrypted = false,
|
||||
@@ -53,19 +63,22 @@ class LocalStorage(
|
||||
)
|
||||
setEncInfo(enc)
|
||||
}
|
||||
_encInfo.value = enc
|
||||
finally {
|
||||
reader?.close()
|
||||
}
|
||||
_metaInfo.value = _metaInfo.value.copy(encInfo = enc)
|
||||
}
|
||||
|
||||
suspend fun setEncInfo(enc: StorageEncryptionInfo) {
|
||||
suspend fun setEncInfo(enc: StorageEncryptionInfo) = withContext(ioDispatcher) {
|
||||
val writer = _accessor.openWriteSystemFile(encInfoFileName)
|
||||
try {
|
||||
_jackson.writeValue(writer, enc)
|
||||
jackson.writeValue(writer, enc)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
TODO("Это никогда не должно произойти")
|
||||
}
|
||||
writer.close()
|
||||
_encInfo.value = enc
|
||||
_metaInfo.value = _metaInfo.value.copy(encInfo = enc)
|
||||
}
|
||||
|
||||
override suspend fun rename(newName: String) {
|
||||
@@ -74,6 +87,6 @@ class LocalStorage(
|
||||
|
||||
companion object {
|
||||
const val ENC_INFO_FILE_POSTFIX = ".enc-info"
|
||||
private val _jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
|
||||
private val jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.github.nullptroma.wallenc.data.vaults.local
|
||||
import com.fasterxml.jackson.core.JacksonException
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.github.nullptroma.wallenc.data.utils.CloseHandledStreamExtension.Companion.onClose
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonDirectory
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonFile
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonMetaInfo
|
||||
@@ -13,6 +14,7 @@ import com.github.nullptroma.wallenc.domain.interfaces.IFile
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo
|
||||
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.MutableStateFlow
|
||||
@@ -20,6 +22,7 @@ import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
@@ -115,7 +118,7 @@ class LocalStorageAccessor(
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private val _jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
|
||||
private val jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
|
||||
|
||||
fun fromFile(filesystemBasePath: Path, file: File): LocalStorageFilePair? {
|
||||
if (!file.exists())
|
||||
@@ -140,19 +143,19 @@ class LocalStorageAccessor(
|
||||
size = filePath.fileSize(),
|
||||
path = storageFilePath
|
||||
)
|
||||
_jackson.writeValue(metaFile, metaInfo)
|
||||
jackson.writeValue(metaFile, metaInfo)
|
||||
} else {
|
||||
var readMeta: CommonMetaInfo
|
||||
try {
|
||||
val reader = metaFile.bufferedReader()
|
||||
readMeta = _jackson.readValue(reader)
|
||||
readMeta = jackson.readValue(reader)
|
||||
} catch (e: JacksonException) {
|
||||
// если файл повреждён - пересоздать
|
||||
readMeta = CommonMetaInfo(
|
||||
size = filePath.fileSize(),
|
||||
path = storageFilePath
|
||||
)
|
||||
_jackson.writeValue(metaFile, readMeta)
|
||||
jackson.writeValue(metaFile, readMeta)
|
||||
}
|
||||
metaInfo = readMeta
|
||||
}
|
||||
@@ -171,7 +174,7 @@ class LocalStorageAccessor(
|
||||
var pair: LocalStorageFilePair? = null
|
||||
try {
|
||||
val reader = metaFile.bufferedReader()
|
||||
val metaInfo: CommonMetaInfo = _jackson.readValue(reader)
|
||||
val metaInfo: CommonMetaInfo = jackson.readValue(reader)
|
||||
val pathString = Path(filesystemBasePath.pathString, metaInfo.path).pathString
|
||||
val file = File(pathString)
|
||||
if (!file.exists()) {
|
||||
@@ -258,8 +261,8 @@ class LocalStorageAccessor(
|
||||
var size = 0L
|
||||
var numOfFiles = 0
|
||||
|
||||
scanStorage(baseStoragePath = "/", maxDepth = -1, fileCallback = { _, CommonFile ->
|
||||
size += CommonFile.metaInfo.size
|
||||
scanStorage(baseStoragePath = "/", maxDepth = -1, fileCallback = { _, commonFile ->
|
||||
size += commonFile.metaInfo.size
|
||||
numOfFiles++
|
||||
|
||||
if (numOfFiles % DATA_PAGE_LENGTH == 0) {
|
||||
@@ -408,9 +411,11 @@ class LocalStorageAccessor(
|
||||
writeMeta(pair.metaFile, newMeta)
|
||||
_filesUpdates.emit(
|
||||
DataPage(
|
||||
list = listOf(CommonFile(
|
||||
list = listOf(
|
||||
CommonFile(
|
||||
metaInfo = newMeta
|
||||
)),
|
||||
)
|
||||
),
|
||||
pageLength = 1,
|
||||
pageIndex = 0
|
||||
)
|
||||
@@ -419,31 +424,41 @@ class LocalStorageAccessor(
|
||||
|
||||
|
||||
private fun writeMeta(metaFile: File, meta: IMetaInfo) {
|
||||
_jackson.writeValue(metaFile, meta)
|
||||
jackson.writeValue(metaFile, meta)
|
||||
}
|
||||
|
||||
private fun createFile(storagePath: String): CommonFile {
|
||||
private suspend fun createFile(storagePath: String): CommonFile = withContext(ioDispatcher) {
|
||||
val path = Path(_filesystemBasePath.pathString, storagePath)
|
||||
val file = path.toFile()
|
||||
if (file.exists() && file.isDirectory) {
|
||||
throw Exception("Что то пошло не так") // TODO
|
||||
} else {
|
||||
} else if(!file.exists()) {
|
||||
file.createNewFile()
|
||||
|
||||
val cur = _numberOfFiles.value
|
||||
_numberOfFiles.value = if (cur == null) null else cur + 1
|
||||
}
|
||||
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, file)
|
||||
?: throw Exception("Что то пошло не так") // TODO
|
||||
val newMeta = pair.meta.copy(lastModified = Clock.systemUTC().instant())
|
||||
writeMeta(pair.metaFile, newMeta)
|
||||
return CommonFile(newMeta)
|
||||
_filesUpdates.emit(
|
||||
DataPage(
|
||||
list = listOf(CommonFile(pair.meta)),
|
||||
pageLength = 1,
|
||||
pageIndex = 0
|
||||
)
|
||||
)
|
||||
return@withContext CommonFile(newMeta)
|
||||
}
|
||||
|
||||
private fun createDir(storagePath: String): CommonDirectory {
|
||||
private suspend fun createDir(storagePath: String): CommonDirectory = withContext(ioDispatcher) {
|
||||
val path = Path(_filesystemBasePath.pathString, storagePath)
|
||||
val file = path.toFile()
|
||||
if (file.exists() && !file.isDirectory) {
|
||||
throw Exception("Что то пошло не так") // TODO
|
||||
} else {
|
||||
} else if(!file.exists()) {
|
||||
Files.createDirectories(path)
|
||||
}
|
||||
|
||||
@@ -451,11 +466,25 @@ class LocalStorageAccessor(
|
||||
?: throw Exception("Что то пошло не так") // TODO
|
||||
val newMeta = pair.meta.copy(lastModified = Clock.systemUTC().instant())
|
||||
writeMeta(pair.metaFile, newMeta)
|
||||
return CommonDirectory(newMeta, 0)
|
||||
_dirsUpdates.emit(
|
||||
DataPage(
|
||||
list = listOf(CommonDirectory(pair.meta, null)),
|
||||
pageLength = 1,
|
||||
pageIndex = 0
|
||||
)
|
||||
)
|
||||
return@withContext CommonDirectory(newMeta, 0)
|
||||
}
|
||||
|
||||
override suspend fun touchFile(path: String): Unit = withContext(ioDispatcher) {
|
||||
createFile(path)
|
||||
|
||||
// перебор все каталогов и обновление их времени модификации
|
||||
var parent = Path(path).parent
|
||||
while(parent != null) {
|
||||
touchDir(parent.pathString)
|
||||
parent = parent.parent
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun touchDir(path: String): Unit = withContext(ioDispatcher) {
|
||||
@@ -474,7 +503,11 @@ class LocalStorageAccessor(
|
||||
touchFile(path)
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, path)
|
||||
?: throw Exception("Файла нет") // TODO
|
||||
return@withContext pair.file.outputStream()
|
||||
return@withContext pair.file.outputStream().onClose {
|
||||
CoroutineScope(ioDispatcher).launch {
|
||||
touchFile(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun openRead(path: String): InputStream = withContext(ioDispatcher) {
|
||||
@@ -519,6 +552,6 @@ class LocalStorageAccessor(
|
||||
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() }
|
||||
private val jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
|
||||
}
|
||||
}
|
||||
@@ -34,18 +34,18 @@ class LocalVault(private val ioDispatcher: CoroutineDispatcher, context: Context
|
||||
private val _availableSpace = MutableStateFlow(null)
|
||||
override val availableSpace: StateFlow<Int?> = _availableSpace
|
||||
|
||||
private val _path = MutableStateFlow<File?>(null)
|
||||
private val path = MutableStateFlow<File?>(null)
|
||||
|
||||
init {
|
||||
CoroutineScope(ioDispatcher).launch {
|
||||
_path.value = context.getExternalFilesDir("LocalVault")
|
||||
_isAvailable.value = _path.value != null
|
||||
path.value = context.getExternalFilesDir("LocalVault")
|
||||
_isAvailable.value = path.value != null
|
||||
readStorages()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun readStorages() {
|
||||
val path = _path.value
|
||||
val path = path.value
|
||||
if (path == null || !_isAvailable.value)
|
||||
return
|
||||
|
||||
@@ -59,7 +59,7 @@ class LocalVault(private val ioDispatcher: CoroutineDispatcher, context: Context
|
||||
}
|
||||
|
||||
override suspend fun createStorage(): LocalStorage = withContext(ioDispatcher) {
|
||||
val path = _path.value
|
||||
val path = path.value
|
||||
if (path == null || !_isAvailable.value)
|
||||
throw Exception("Not available")
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.github.nullptroma.wallenc.domain.common.impl
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorageMetaInfo
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
|
||||
|
||||
data class CommonStorageMetaInfo(
|
||||
override val encInfo: StorageEncryptionInfo?,
|
||||
override val name: String?,
|
||||
override val lastModified: Instant = Clock.systemUTC().instant()
|
||||
) : IStorageMetaInfo
|
||||
@@ -2,9 +2,9 @@ package com.github.nullptroma.wallenc.domain.datatypes
|
||||
|
||||
class DataPage<T>(
|
||||
list: List<T>,
|
||||
isLoading: Boolean? = false,
|
||||
isError: Boolean? = false,
|
||||
val hasNext: Boolean? = false,
|
||||
isLoading: Boolean? = null,
|
||||
isError: Boolean? = null,
|
||||
val hasNext: Boolean? = null,
|
||||
val pageLength: Int,
|
||||
val pageIndex: Int
|
||||
) : DataPackage<List<T>>(data = list, isLoading = isLoading, isError = isError)
|
||||
@@ -1,38 +1,46 @@
|
||||
package com.github.nullptroma.wallenc.domain.encrypt
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.ILogger
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorageMetaInfo
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.DisposableHandle
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import java.util.UUID
|
||||
|
||||
class EncryptedStorage(
|
||||
private val source: IStorage,
|
||||
private val _source: IStorage,
|
||||
key: EncryptKey,
|
||||
ioDispatcher: CoroutineDispatcher,
|
||||
private val ioDispatcher: CoroutineDispatcher,
|
||||
override val uuid: UUID = UUID.randomUUID()
|
||||
) : IStorage, DisposableHandle {
|
||||
override val size: StateFlow<Long?>
|
||||
get() = source.size
|
||||
get() = _source.size
|
||||
override val numberOfFiles: StateFlow<Int?>
|
||||
get() = source.numberOfFiles
|
||||
override val name: StateFlow<String>
|
||||
get() = TODO("Not yet implemented")
|
||||
override val isAvailable: StateFlow<Boolean>
|
||||
get() = source.isAvailable
|
||||
override val encInfo: StateFlow<StorageEncryptionInfo?>
|
||||
get() = MutableStateFlow(
|
||||
StorageEncryptionInfo(
|
||||
get() = _source.numberOfFiles
|
||||
|
||||
private val _metaInfo = MutableStateFlow<IStorageMetaInfo>(
|
||||
CommonStorageMetaInfo(
|
||||
encInfo = StorageEncryptionInfo(
|
||||
isEncrypted = false,
|
||||
encryptedTestData = null
|
||||
),
|
||||
name = null
|
||||
)
|
||||
)
|
||||
override val metaInfo: StateFlow<IStorageMetaInfo>
|
||||
get() = _metaInfo
|
||||
|
||||
override val isAvailable: StateFlow<Boolean>
|
||||
get() = _source.isAvailable
|
||||
override val accessor: EncryptedStorageAccessor =
|
||||
EncryptedStorageAccessor(source.accessor, key, ioDispatcher)
|
||||
EncryptedStorageAccessor(_source.accessor, key, ioDispatcher)
|
||||
|
||||
override suspend fun rename(newName: String) {
|
||||
TODO("Not yet implemented")
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.github.nullptroma.wallenc.domain.datatypes.DataPackage
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||
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.IMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorageAccessor
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
@@ -30,8 +29,8 @@ class EncryptedStorageAccessor(
|
||||
key: EncryptKey,
|
||||
ioDispatcher: CoroutineDispatcher
|
||||
) : IStorageAccessor, DisposableHandle {
|
||||
private val _job = Job()
|
||||
private val _scope = CoroutineScope(ioDispatcher + _job)
|
||||
private val job = Job()
|
||||
private val scope = CoroutineScope(ioDispatcher + job)
|
||||
|
||||
override val size: StateFlow<Long?> = source.size
|
||||
override val numberOfFiles: StateFlow<Int?> = source.numberOfFiles
|
||||
@@ -43,14 +42,14 @@ class EncryptedStorageAccessor(
|
||||
private val _dirsUpdates = MutableSharedFlow<DataPackage<List<IDirectory>>>()
|
||||
override val dirsUpdates: SharedFlow<DataPackage<List<IDirectory>>> = _dirsUpdates
|
||||
|
||||
private val _encryptor = Encryptor(key.toAesKey())
|
||||
private val encryptor = Encryptor(key.toAesKey())
|
||||
|
||||
init {
|
||||
collectSourceState()
|
||||
}
|
||||
|
||||
private fun collectSourceState() {
|
||||
_scope.launch {
|
||||
scope.launch {
|
||||
launch {
|
||||
source.filesUpdates.collect {
|
||||
val files = it.data.map(::decryptEntity)
|
||||
@@ -116,7 +115,7 @@ class EncryptedStorageAccessor(
|
||||
val path = Path(pathStr)
|
||||
val segments = mutableListOf<String>()
|
||||
for (segment in path)
|
||||
segments.add(_encryptor.encryptString(segment.pathString))
|
||||
segments.add(encryptor.encryptString(segment.pathString))
|
||||
val res = Path("/",*(segments.toTypedArray()))
|
||||
return res.pathString
|
||||
}
|
||||
@@ -125,7 +124,7 @@ class EncryptedStorageAccessor(
|
||||
val path = Path(pathStr)
|
||||
val segments = mutableListOf<String>()
|
||||
for (segment in path)
|
||||
segments.add(_encryptor.decryptString(segment.pathString))
|
||||
segments.add(encryptor.decryptString(segment.pathString))
|
||||
val res = Path("/",*(segments.toTypedArray()))
|
||||
return res.pathString
|
||||
}
|
||||
@@ -198,12 +197,12 @@ class EncryptedStorageAccessor(
|
||||
|
||||
override suspend fun openWrite(path: String): OutputStream {
|
||||
val stream = source.openWrite(encryptPath(path))
|
||||
return _encryptor.encryptStream(stream)
|
||||
return encryptor.encryptStream(stream)
|
||||
}
|
||||
|
||||
override suspend fun openRead(path: String): InputStream {
|
||||
val stream = source.openRead(encryptPath(path))
|
||||
return _encryptor.decryptStream(stream)
|
||||
return encryptor.decryptStream(stream)
|
||||
}
|
||||
|
||||
override suspend fun moveToTrash(path: String) {
|
||||
@@ -211,7 +210,8 @@ class EncryptedStorageAccessor(
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
_job.cancel()
|
||||
_encryptor.dispose()
|
||||
job.cancel()
|
||||
encryptor.dispose()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.github.nullptroma.wallenc.domain.interfaces
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.util.UUID
|
||||
|
||||
@@ -9,6 +8,5 @@ sealed interface IStorageInfo {
|
||||
val isAvailable: StateFlow<Boolean>
|
||||
val size: StateFlow<Long?>
|
||||
val numberOfFiles: StateFlow<Int?>
|
||||
val encInfo: StateFlow<StorageEncryptionInfo?>
|
||||
val name: StateFlow<String?>
|
||||
val metaInfo: StateFlow<IStorageMetaInfo>
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.github.nullptroma.wallenc.domain.interfaces
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
|
||||
interface IStorageMetaInfo {
|
||||
val encInfo: StorageEncryptionInfo?
|
||||
val name: String?
|
||||
val lastModified: Instant
|
||||
|
||||
}
|
||||
@@ -3,5 +3,5 @@ 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} }"
|
||||
return "{ uuid: $uuid, enc: ${metaInfo.value.encInfo} }"
|
||||
}
|
||||
@@ -48,7 +48,9 @@ fun LocalVaultScreen(modifier: Modifier = Modifier,
|
||||
val available by it.isAvailable.collectAsStateWithLifecycle()
|
||||
val numOfFiles by it.numberOfFiles.collectAsStateWithLifecycle()
|
||||
val size by it.size.collectAsStateWithLifecycle()
|
||||
val enc by it.encInfo.collectAsStateWithLifecycle()
|
||||
val metaInfo by it.metaInfo.collectAsStateWithLifecycle()
|
||||
|
||||
val enc = metaInfo.encInfo
|
||||
Column {
|
||||
Text(it.uuid.toString())
|
||||
Text("IsAvailable: $available")
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCas
|
||||
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.flow.collect
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -21,15 +20,15 @@ import kotlin.system.measureTimeMillis
|
||||
|
||||
@HiltViewModel
|
||||
class LocalVaultViewModel @Inject constructor(
|
||||
private val _manageLocalVaultUseCase: ManageLocalVaultUseCase,
|
||||
private val _getOpenedStoragesUseCase: GetOpenedStoragesUseCase,
|
||||
private val _storageFileManagementUseCase: StorageFileManagementUseCase,
|
||||
private val manageLocalVaultUseCase: ManageLocalVaultUseCase,
|
||||
private val getOpenedStoragesUseCase: GetOpenedStoragesUseCase,
|
||||
private val storageFileManagementUseCase: StorageFileManagementUseCase,
|
||||
private val logger: ILogger
|
||||
) :
|
||||
ViewModelBase<LocalVaultScreenState>(LocalVaultScreenState(listOf())) {
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
_manageLocalVaultUseCase.localStorages.combine(_getOpenedStoragesUseCase.openedStorages) { local, opened ->
|
||||
manageLocalVaultUseCase.localStorages.combine(getOpenedStoragesUseCase.openedStorages) { local, opened ->
|
||||
local + (opened?.map { it.value } ?: listOf())
|
||||
}.collectLatest {
|
||||
val newState = state.value.copy(
|
||||
@@ -41,13 +40,13 @@ class LocalVaultViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun printStorageInfoToLog(storage: IStorageInfo) {
|
||||
_storageFileManagementUseCase.setStorage(storage)
|
||||
storageFileManagementUseCase.setStorage(storage)
|
||||
viewModelScope.launch {
|
||||
val files: List<IFile>
|
||||
val dirs: List<IDirectory>
|
||||
val time = measureTimeMillis {
|
||||
files = _storageFileManagementUseCase.getAllFiles()
|
||||
dirs = _storageFileManagementUseCase.getAllDirs()
|
||||
files = storageFileManagementUseCase.getAllFiles()
|
||||
dirs = storageFileManagementUseCase.getAllDirs()
|
||||
}
|
||||
for (file in files) {
|
||||
logger.debug("Files", file.metaInfo.toString())
|
||||
@@ -62,7 +61,7 @@ class LocalVaultViewModel @Inject constructor(
|
||||
|
||||
fun createStorage() {
|
||||
viewModelScope.launch {
|
||||
_manageLocalVaultUseCase.createStorage(EncryptKey("hello"))
|
||||
manageLocalVaultUseCase.createStorage(EncryptKey("hello"))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user