Мета информация о хранилищах хранится в бд

This commit is contained in:
Пытков Роман
2025-01-25 20:19:06 +03:00
parent 8e2ac2f68d
commit d78ed70e0f
25 changed files with 328 additions and 135 deletions

View File

@@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() { fun useAppContext() {
// Context of the app under test. // Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.github.nullptroma.wallenc", appContext.packageName) assertEquals("com.github.nullptroma.wallenc.app", appContext.packageName)
} }
} }

View File

@@ -4,6 +4,7 @@ import android.content.Context
import com.github.nullptroma.wallenc.data.db.RoomFactory import com.github.nullptroma.wallenc.data.db.RoomFactory
import com.github.nullptroma.wallenc.data.db.app.IAppDb import com.github.nullptroma.wallenc.data.db.app.IAppDb
import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao
import com.github.nullptroma.wallenc.data.db.app.dao.StorageMetaInfoDao
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@@ -25,6 +26,12 @@ class RoomModule {
return database.storageKeyMapDao return database.storageKeyMapDao
} }
@Provides
@Singleton
fun provideStorageMetaInfoDao(database: IAppDb): StorageMetaInfoDao {
return database.storageMetaInfoDao
}
@Provides @Provides
@Singleton @Singleton
fun provideAppDb( fun provideAppDb(

View File

@@ -3,8 +3,10 @@ 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.db.app.dao.StorageKeyMapDao import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao
import com.github.nullptroma.wallenc.data.db.app.dao.StorageMetaInfoDao
import com.github.nullptroma.wallenc.data.db.app.repository.StorageKeyMapRepository import com.github.nullptroma.wallenc.data.db.app.repository.StorageKeyMapRepository
import com.github.nullptroma.wallenc.data.vaults.UnlockManager import com.github.nullptroma.wallenc.data.db.app.repository.StorageMetaInfoRepository
import com.github.nullptroma.wallenc.data.storages.UnlockManager
import com.github.nullptroma.wallenc.data.vaults.VaultsManager import com.github.nullptroma.wallenc.data.vaults.VaultsManager
import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
@@ -21,24 +23,42 @@ import javax.inject.Singleton
class SingletonModule { class SingletonModule {
@Provides @Provides
@Singleton @Singleton
fun provideVaultsManager(@IoDispatcher ioDispatcher: CoroutineDispatcher, fun provideVaultsManager(
@ApplicationContext context: Context): IVaultsManager { @IoDispatcher ioDispatcher: CoroutineDispatcher,
@ApplicationContext context: Context
): IVaultsManager {
return VaultsManager(ioDispatcher, context) return VaultsManager(ioDispatcher, context)
} }
@Provides @Provides
@Singleton @Singleton
fun provideStorageKeyMapRepository(dao: StorageKeyMapDao): StorageKeyMapRepository { fun provideStorageKeyMapRepository(
return StorageKeyMapRepository(dao) dao: StorageKeyMapDao,
@IoDispatcher ioDispatcher: CoroutineDispatcher
): StorageKeyMapRepository {
return StorageKeyMapRepository(dao, ioDispatcher)
} }
@Provides @Provides
@Singleton @Singleton
fun provideUnlockManager(@IoDispatcher ioDispatcher: CoroutineDispatcher, fun provideStorageMetaInfoRepository(
repo: StorageKeyMapRepository, dao: StorageMetaInfoDao,
vaultsManager: IVaultsManager): IUnlockManager { @IoDispatcher ioDispatcher: CoroutineDispatcher
): StorageMetaInfoRepository {
return StorageMetaInfoRepository(dao, ioDispatcher)
}
@Provides
@Singleton
fun provideUnlockManager(
@IoDispatcher ioDispatcher: CoroutineDispatcher,
keyRepo: StorageKeyMapRepository,
metaRepo: StorageMetaInfoRepository,
vaultsManager: IVaultsManager
): IUnlockManager {
return UnlockManager( return UnlockManager(
repo = repo, keymapRepository = keyRepo,
metaInfoRepository = metaRepo,
ioDispatcher = ioDispatcher, ioDispatcher = ioDispatcher,
vaultsManager = vaultsManager vaultsManager = vaultsManager
) )

View File

@@ -1,6 +1,5 @@
package com.github.nullptroma.wallenc.app.di.modules.domain package com.github.nullptroma.wallenc.app.di.modules.domain
import com.github.nullptroma.wallenc.data.vaults.UnlockManager
import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase

View File

@@ -3,13 +3,17 @@ package com.github.nullptroma.wallenc.data.db.app
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao
import com.github.nullptroma.wallenc.data.db.app.dao.StorageMetaInfoDao
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKeyMap import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKeyMap
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageMetaInfo
interface IAppDb { interface IAppDb {
val storageKeyMapDao: StorageKeyMapDao val storageKeyMapDao: StorageKeyMapDao
val storageMetaInfoDao: StorageMetaInfoDao
} }
@Database(entities = [DbStorageKeyMap::class], version = 2, exportSchema = false) @Database(entities = [DbStorageKeyMap::class, DbStorageMetaInfo::class], version = 2, exportSchema = false)
abstract class AppDb : IAppDb, RoomDatabase() { abstract class AppDb : IAppDb, RoomDatabase() {
abstract override val storageKeyMapDao: StorageKeyMapDao abstract override val storageKeyMapDao: StorageKeyMapDao
abstract override val storageMetaInfoDao: StorageMetaInfoDao
} }

View File

@@ -12,11 +12,11 @@ import kotlinx.coroutines.flow.StateFlow
@Dao @Dao
interface StorageKeyMapDao { interface StorageKeyMapDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun add(keymap: DbStorageKeyMap) suspend fun add(keymap: DbStorageKeyMap)
@Query("SELECT * FROM storage_key_maps") @Query("SELECT * FROM storage_key_maps")
fun getAll(): List<DbStorageKeyMap> suspend fun getAll(): List<DbStorageKeyMap>
@Delete @Delete
fun delete(keymap: DbStorageKeyMap) suspend fun delete(keymap: DbStorageKeyMap)
} }

View File

@@ -0,0 +1,35 @@
package com.github.nullptroma.wallenc.data.db.app.dao
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKeyMap
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageMetaInfo
import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import java.util.UUID
@Dao
interface StorageMetaInfoDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun add(metaInfo: DbStorageMetaInfo)
@Query("SELECT * FROM storage_meta_infos")
suspend fun getAll(): List<DbStorageMetaInfo>
@Query("SELECT * FROM storage_meta_infos")
fun getAllFlow(): Flow<List<DbStorageMetaInfo>>
@Query("SELECT * FROM storage_meta_infos WHERE uuid == :uuid")
fun getMetaInfoFlow(uuid: UUID): Flow<DbStorageMetaInfo>
@Query("SELECT * FROM storage_meta_infos WHERE uuid == :uuid")
suspend fun getMetaInfo(uuid: UUID): DbStorageMetaInfo?
@Delete
suspend fun delete(metaInfo: DbStorageMetaInfo)
}

View File

@@ -9,14 +9,14 @@ import java.util.UUID
@Entity(tableName = "storage_key_maps", primaryKeys = [ "source_uuid", "dest_uuid" ]) @Entity(tableName = "storage_key_maps", primaryKeys = [ "source_uuid", "dest_uuid" ])
data class DbStorageKeyMap( data class DbStorageKeyMap(
@ColumnInfo(name = "source_uuid") val sourceUuid: String, @ColumnInfo(name = "source_uuid") val sourceUuid: UUID,
@ColumnInfo(name = "dest_uuid") val destUuid: String, @ColumnInfo(name = "dest_uuid") val destUuid: UUID,
@ColumnInfo(name = "key") val key: ByteArray @ColumnInfo(name = "key") val key: ByteArray
) { ) {
fun toModel(): StorageKeyMap { fun toModel(): StorageKeyMap {
return StorageKeyMap( return StorageKeyMap(
sourceUuid = UUID.fromString(sourceUuid), sourceUuid = sourceUuid,
destUuid = UUID.fromString(destUuid), destUuid = destUuid,
key = EncryptKey(key) key = EncryptKey(key)
) )
} }
@@ -44,8 +44,8 @@ data class DbStorageKeyMap(
companion object { companion object {
fun fromModel(keymap: StorageKeyMap): DbStorageKeyMap { fun fromModel(keymap: StorageKeyMap): DbStorageKeyMap {
return DbStorageKeyMap( return DbStorageKeyMap(
sourceUuid = keymap.sourceUuid.toString(), sourceUuid = keymap.sourceUuid,
destUuid = keymap.destUuid.toString(), destUuid = keymap.destUuid,
key = keymap.key.bytes key = keymap.key.bytes
) )
} }

View File

@@ -0,0 +1,13 @@
package com.github.nullptroma.wallenc.data.db.app.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo
import java.util.UUID
@Entity(tableName = "storage_meta_infos")
data class DbStorageMetaInfo(
@PrimaryKey @ColumnInfo(name = "uuid") val uuid: UUID,
@ColumnInfo(name = "meta_info") val metaInfoJson: String
)

View File

@@ -3,14 +3,20 @@ package com.github.nullptroma.wallenc.data.db.app.repository
import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKeyMap import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKeyMap
import com.github.nullptroma.wallenc.data.model.StorageKeyMap import com.github.nullptroma.wallenc.data.model.StorageKeyMap
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
class StorageKeyMapRepository(private val dao: StorageKeyMapDao) { class StorageKeyMapRepository(
fun getAll() = dao.getAll().map { it.toModel() } private val dao: StorageKeyMapDao,
fun add(keymap: StorageKeyMap) { private val ioDispatcher: CoroutineDispatcher
) {
suspend fun getAll() = withContext(ioDispatcher) { dao.getAll().map { it.toModel() } }
suspend fun add(keymap: StorageKeyMap) = withContext(ioDispatcher) {
val dbModel = DbStorageKeyMap.fromModel(keymap) val dbModel = DbStorageKeyMap.fromModel(keymap)
dao.add(dbModel) dao.add(dbModel)
} }
fun delete(keymap: StorageKeyMap) {
suspend fun delete(keymap: StorageKeyMap) = withContext(ioDispatcher) {
val dbModel = DbStorageKeyMap.fromModel(keymap) val dbModel = DbStorageKeyMap.fromModel(keymap)
dao.delete(dbModel) dao.delete(dbModel)
} }

View File

@@ -0,0 +1,52 @@
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.domain.common.impl.CommonStorageMetaInfo
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import java.util.UUID
class StorageMetaInfoRepository(
private val dao: StorageMetaInfoDao,
private val ioDispatcher: CoroutineDispatcher
) {
fun getAllFlow() = dao.getAllFlow()
suspend fun getAll() = withContext(ioDispatcher) { dao.getAll() }
suspend fun getMeta(uuid: UUID): CommonStorageMetaInfo? = withContext(ioDispatcher) {
val json = dao.getMetaInfo(uuid)?.metaInfoJson ?: return@withContext null
return@withContext jackson.readValue(
json,
CommonStorageMetaInfo::class.java
)
}
fun observeMeta(uuid: UUID): Flow<CommonStorageMetaInfo> {
return dao.getMetaInfoFlow(uuid)
.map { jackson.readValue(it.metaInfoJson, CommonStorageMetaInfo::class.java) }
}
suspend fun setMeta(uuid: UUID, metaInfo: CommonStorageMetaInfo) = withContext(ioDispatcher) {
val json = jackson.writeValueAsString(metaInfo)
dao.add(DbStorageMetaInfo(uuid, json))
}
fun createSingleStorageProvider(uuid: UUID): SingleStorageMetaInfoProvider {
return SingleStorageMetaInfoProvider(this, uuid)
}
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)
}
companion object {
private val jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
}
}

View File

@@ -1,9 +1,10 @@
package com.github.nullptroma.wallenc.data.vaults package com.github.nullptroma.wallenc.data.storages
import com.github.nullptroma.wallenc.data.db.app.repository.StorageKeyMapRepository import com.github.nullptroma.wallenc.data.db.app.repository.StorageKeyMapRepository
import com.github.nullptroma.wallenc.data.db.app.repository.StorageMetaInfoRepository
import com.github.nullptroma.wallenc.data.model.StorageKeyMap import com.github.nullptroma.wallenc.data.model.StorageKeyMap
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
import com.github.nullptroma.wallenc.domain.encrypt.EncryptedStorage import com.github.nullptroma.wallenc.data.storages.encrypt.EncryptedStorage
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor import com.github.nullptroma.wallenc.domain.encrypt.Encryptor
import com.github.nullptroma.wallenc.domain.interfaces.IStorage import com.github.nullptroma.wallenc.domain.interfaces.IStorage
import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager
@@ -20,7 +21,8 @@ import kotlinx.coroutines.withContext
import java.util.UUID import java.util.UUID
class UnlockManager( class UnlockManager(
private val repo: StorageKeyMapRepository, private val keymapRepository: StorageKeyMapRepository,
private val metaInfoRepository: StorageMetaInfoRepository,
private val ioDispatcher: CoroutineDispatcher, private val ioDispatcher: CoroutineDispatcher,
vaultsManager: IVaultsManager vaultsManager: IVaultsManager
) : IUnlockManager { ) : IUnlockManager {
@@ -33,7 +35,7 @@ class UnlockManager(
CoroutineScope(ioDispatcher).launch { CoroutineScope(ioDispatcher).launch {
vaultsManager.allStorages.collectLatest { vaultsManager.allStorages.collectLatest {
mutex.lock() mutex.lock()
val allKeys = repo.getAll() val allKeys = keymapRepository.getAll()
val allStorages = it.associateBy({ it.uuid }, { it }) val allStorages = it.associateBy({ it.uuid }, { it })
val map = _openedStorages.value?.toMutableMap() ?: mutableMapOf() val map = _openedStorages.value?.toMutableMap() ?: mutableMapOf()
for(keymap in allKeys) { for(keymap in allKeys) {
@@ -49,11 +51,12 @@ class UnlockManager(
} }
} }
private fun createEncryptedStorage(storage: IStorage, key: EncryptKey, uuid: UUID): EncryptedStorage { private suspend fun createEncryptedStorage(storage: IStorage, key: EncryptKey, uuid: UUID): EncryptedStorage {
return EncryptedStorage( return EncryptedStorage.create(
_source = storage, source = storage,
key = key, key = key,
ioDispatcher = ioDispatcher, ioDispatcher = ioDispatcher,
metaInfoProvider = metaInfoRepository.createSingleStorageProvider(uuid),
uuid = uuid uuid = uuid
) )
} }
@@ -80,7 +83,7 @@ class UnlockManager(
val encStorage = createEncryptedStorage(storage, keymap.key, keymap.destUuid) val encStorage = createEncryptedStorage(storage, keymap.key, keymap.destUuid)
opened[storage.uuid] = encStorage opened[storage.uuid] = encStorage
_openedStorages.value = opened _openedStorages.value = opened
repo.add(keymap) keymapRepository.add(keymap)
mutex.unlock() mutex.unlock()
} }
@@ -97,7 +100,7 @@ class UnlockManager(
remove(storage.uuid) remove(storage.uuid)
} }
enc.dispose() enc.dispose()
repo.delete(model) keymapRepository.delete(model)
mutex.unlock() mutex.unlock()
} }
} }

View File

@@ -0,0 +1,96 @@
package com.github.nullptroma.wallenc.data.storages.encrypt
import com.github.nullptroma.wallenc.data.db.app.repository.StorageMetaInfoRepository
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.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.StateFlow
import kotlinx.coroutines.withContext
import java.util.UUID
class EncryptedStorage private constructor(
private val source: IStorage,
key: EncryptKey,
private val ioDispatcher: CoroutineDispatcher,
private val metaInfoProvider: StorageMetaInfoRepository.SingleStorageMetaInfoProvider,
override val uuid: UUID = UUID.randomUUID()
) : IStorage, DisposableHandle {
override val size: StateFlow<Long?>
get() = source.size
override val numberOfFiles: StateFlow<Int?>
get() = source.numberOfFiles
private val _metaInfo = MutableStateFlow<IStorageMetaInfo>(
CommonStorageMetaInfo()
)
override val metaInfo: StateFlow<IStorageMetaInfo>
get() = _metaInfo
override val isVirtualStorage: Boolean = true
override val isAvailable: StateFlow<Boolean>
get() = source.isAvailable
override val accessor: EncryptedStorageAccessor =
EncryptedStorageAccessor(source.accessor, key, ioDispatcher)
private suspend fun init() {
readMeta()
}
private suspend fun readMeta() = withContext(ioDispatcher) {
var meta = metaInfoProvider.get()
if(meta == null) {
meta = CommonStorageMetaInfo()
metaInfoProvider.set(meta)
}
_metaInfo.value = meta
}
override suspend fun rename(newName: String) = withContext(ioDispatcher) {
val cur = _metaInfo.value
val newMeta = CommonStorageMetaInfo(
encInfo = cur.encInfo,
name = newName
)
_metaInfo.value = newMeta
metaInfoProvider.set(newMeta)
}
override suspend fun setEncInfo(encInfo: StorageEncryptionInfo) = withContext(ioDispatcher) {
val cur = _metaInfo.value
val newMeta = CommonStorageMetaInfo(
encInfo = encInfo,
name = cur.name
)
_metaInfo.value = newMeta
metaInfoProvider.set(newMeta)
}
override fun dispose() {
accessor.dispose()
}
companion object {
suspend fun create(
source: IStorage,
key: EncryptKey,
ioDispatcher: CoroutineDispatcher,
metaInfoProvider: StorageMetaInfoRepository.SingleStorageMetaInfoProvider,
uuid: UUID = UUID.randomUUID()
): EncryptedStorage = withContext(ioDispatcher) {
val storage = EncryptedStorage(
source = source,
key = key,
ioDispatcher = ioDispatcher,
metaInfoProvider = metaInfoProvider,
uuid = uuid
)
storage.init()
return@withContext storage
}
}
}

View File

@@ -1,10 +1,11 @@
package com.github.nullptroma.wallenc.domain.encrypt package com.github.nullptroma.wallenc.data.storages.encrypt
import com.github.nullptroma.wallenc.domain.common.impl.CommonDirectory 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.CommonFile
import com.github.nullptroma.wallenc.domain.common.impl.CommonMetaInfo import com.github.nullptroma.wallenc.domain.common.impl.CommonMetaInfo
import com.github.nullptroma.wallenc.domain.datatypes.DataPackage import com.github.nullptroma.wallenc.domain.datatypes.DataPackage
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor
import com.github.nullptroma.wallenc.domain.interfaces.IDirectory import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
import com.github.nullptroma.wallenc.domain.interfaces.IFile import com.github.nullptroma.wallenc.domain.interfaces.IFile
import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo

View File

@@ -1,4 +1,4 @@
package com.github.nullptroma.wallenc.data.vaults.local package com.github.nullptroma.wallenc.data.storages.local
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo
@@ -24,14 +24,8 @@ class LocalStorage(
override val numberOfFiles: StateFlow<Int?> override val numberOfFiles: StateFlow<Int?>
get() = accessor.numberOfFiles get() = accessor.numberOfFiles
private val _metaInfo = MutableStateFlow( private val _metaInfo = MutableStateFlow<IStorageMetaInfo>(
CommonStorageMetaInfo( CommonStorageMetaInfo()
encInfo = StorageEncryptionInfo(
isEncrypted = false,
encryptedTestData = null
),
name = null
)
) )
override val metaInfo: StateFlow<IStorageMetaInfo> override val metaInfo: StateFlow<IStorageMetaInfo>
get() = _metaInfo get() = _metaInfo
@@ -40,49 +34,60 @@ class LocalStorage(
get() = accessor.isAvailable get() = accessor.isAvailable
private val _accessor = LocalStorageAccessor(absolutePath, ioDispatcher) private val _accessor = LocalStorageAccessor(absolutePath, ioDispatcher)
override val accessor: IStorageAccessor = _accessor override val accessor: IStorageAccessor = _accessor
override val isVirtualStorage: Boolean = false
private val encInfoFileName: String = "$uuid$ENC_INFO_FILE_POSTFIX" private val metaInfoFileName: String = "$uuid$ENC_INFO_FILE_POSTFIX"
suspend fun init() { suspend fun init() {
_accessor.init() _accessor.init()
readEncInfo() readMetaInfo()
} }
private suspend fun readEncInfo() = withContext(ioDispatcher) { private suspend fun readMetaInfo() = withContext(ioDispatcher) {
var enc: StorageEncryptionInfo? = null var meta: CommonStorageMetaInfo
var reader: InputStream? = null var reader: InputStream? = null
try { try {
reader = _accessor.openReadSystemFile(encInfoFileName) reader = _accessor.openReadSystemFile(metaInfoFileName)
enc = jackson.readValue(reader, StorageEncryptionInfo::class.java) meta = jackson.readValue(reader, CommonStorageMetaInfo::class.java)
} }
catch(e: Exception) { catch(e: Exception) {
// чтение не удалось, значит нужно записать файл // чтение не удалось, значит нужно записать файл
enc = StorageEncryptionInfo( meta = CommonStorageMetaInfo()
isEncrypted = false, updateMetaInfo(meta)
encryptedTestData = null
)
setEncInfo(enc)
} }
finally { finally {
reader?.close() reader?.close()
} }
_metaInfo.value = _metaInfo.value.copy(encInfo = enc) _metaInfo.value = meta
} }
suspend fun setEncInfo(enc: StorageEncryptionInfo) = withContext(ioDispatcher) { private suspend fun updateMetaInfo(meta: IStorageMetaInfo) = withContext(ioDispatcher) {
val writer = _accessor.openWriteSystemFile(encInfoFileName) val writer = _accessor.openWriteSystemFile(metaInfoFileName)
try { try {
jackson.writeValue(writer, enc) jackson.writeValue(writer, meta)
} }
catch (e: Exception) { catch (e: Exception) {
TODO("Это никогда не должно произойти") throw e
} }
finally {
writer.close() writer.close()
_metaInfo.value = _metaInfo.value.copy(encInfo = enc) }
_metaInfo.value = meta
} }
override suspend fun rename(newName: String) { override suspend fun rename(newName: String) = withContext(ioDispatcher) {
TODO("Not yet implemented") val curMeta = metaInfo.value
updateMetaInfo(CommonStorageMetaInfo(
encInfo = curMeta.encInfo,
name = newName
))
}
override suspend fun setEncInfo(encInfo: StorageEncryptionInfo) = withContext(ioDispatcher) {
val curMeta = metaInfo.value
updateMetaInfo(CommonStorageMetaInfo(
encInfo = encInfo,
name = curMeta.name
))
} }
companion object { companion object {

View File

@@ -1,4 +1,4 @@
package com.github.nullptroma.wallenc.data.vaults.local package com.github.nullptroma.wallenc.data.storages.local
import com.fasterxml.jackson.core.JacksonException import com.fasterxml.jackson.core.JacksonException
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
@@ -57,7 +57,7 @@ class LocalStorageAccessor(
private val _dirsUpdates = MutableSharedFlow<DataPage<IDirectory>>() private val _dirsUpdates = MutableSharedFlow<DataPage<IDirectory>>()
override val dirsUpdates: SharedFlow<DataPage<IDirectory>> = _dirsUpdates override val dirsUpdates: SharedFlow<DataPage<IDirectory>> = _dirsUpdates
suspend fun init() { suspend fun init() = withContext(ioDispatcher) {
// запускам сканирование хранилища // запускам сканирование хранилища
scanSizeAndNumOfFiles() scanSizeAndNumOfFiles()
} }
@@ -251,11 +251,11 @@ class LocalStorageAccessor(
* Считает файлы и их размер. Не бросает исключения, если файлы недоступны * Считает файлы и их размер. Не бросает исключения, если файлы недоступны
* @throws none Если возникла ошибка, оставляет размер и количества файлов равными null * @throws none Если возникла ошибка, оставляет размер и количества файлов равными null
*/ */
private suspend fun scanSizeAndNumOfFiles() { private suspend fun scanSizeAndNumOfFiles() = withContext(ioDispatcher) {
if (!checkAvailable()) { if (!checkAvailable()) {
_size.value = null _size.value = null
_numberOfFiles.value = null _numberOfFiles.value = null
return return@withContext
} }
var size = 0L var size = 0L
@@ -280,8 +280,8 @@ class LocalStorageAccessor(
return@withContext listOf() return@withContext listOf()
val list = mutableListOf<IFile>() val list = mutableListOf<IFile>()
scanStorage(baseStoragePath = "/", maxDepth = -1, fileCallback = { _, CommonFile -> scanStorage(baseStoragePath = "/", maxDepth = -1, fileCallback = { _, commonFile ->
list.add(CommonFile) list.add(commonFile)
}) })
return@withContext list return@withContext list
} }

View File

@@ -1,6 +1,7 @@
package com.github.nullptroma.wallenc.data.vaults.local package com.github.nullptroma.wallenc.data.vaults
import android.content.Context import android.content.Context
import com.github.nullptroma.wallenc.data.storages.local.LocalStorage
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
import com.github.nullptroma.wallenc.domain.enums.VaultType import com.github.nullptroma.wallenc.domain.enums.VaultType
import com.github.nullptroma.wallenc.domain.interfaces.IStorage import com.github.nullptroma.wallenc.domain.interfaces.IStorage

View File

@@ -1,7 +1,6 @@
package com.github.nullptroma.wallenc.data.vaults package com.github.nullptroma.wallenc.data.vaults
import android.content.Context import android.content.Context
import com.github.nullptroma.wallenc.data.vaults.local.LocalVault
import com.github.nullptroma.wallenc.domain.interfaces.IStorage import com.github.nullptroma.wallenc.domain.interfaces.IStorage
import com.github.nullptroma.wallenc.domain.interfaces.IVault import com.github.nullptroma.wallenc.domain.interfaces.IVault
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager

View File

@@ -9,7 +9,10 @@ import java.time.Instant
data class CommonStorageMetaInfo( data class CommonStorageMetaInfo(
override val encInfo: StorageEncryptionInfo?, override val encInfo: StorageEncryptionInfo = StorageEncryptionInfo(
override val name: String?, isEncrypted = false,
encryptedTestData = null
),
override val name: String? = null,
override val lastModified: Instant = Clock.systemUTC().instant() override val lastModified: Instant = Clock.systemUTC().instant()
) : IStorageMetaInfo ) : IStorageMetaInfo

View File

@@ -1,52 +0,0 @@
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.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,
key: EncryptKey,
private val ioDispatcher: CoroutineDispatcher,
override val uuid: UUID = UUID.randomUUID()
) : IStorage, DisposableHandle {
override val size: StateFlow<Long?>
get() = _source.size
override val numberOfFiles: StateFlow<Int?>
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)
override suspend fun rename(newName: String) {
TODO("Not yet implemented")
}
override fun dispose() {
accessor.dispose()
}
}

View File

@@ -1,7 +1,10 @@
package com.github.nullptroma.wallenc.domain.interfaces package com.github.nullptroma.wallenc.domain.interfaces
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
interface IStorage: IStorageInfo { interface IStorage: IStorageInfo {
val accessor: IStorageAccessor val accessor: IStorageAccessor
suspend fun rename(newName: String) suspend fun rename(newName: String)
suspend fun setEncInfo(encInfo: StorageEncryptionInfo)
} }

View File

@@ -9,4 +9,5 @@ sealed interface IStorageInfo {
val size: StateFlow<Long?> val size: StateFlow<Long?>
val numberOfFiles: StateFlow<Int?> val numberOfFiles: StateFlow<Int?>
val metaInfo: StateFlow<IStorageMetaInfo> val metaInfo: StateFlow<IStorageMetaInfo>
val isVirtualStorage: Boolean
} }

View File

@@ -1,12 +1,10 @@
package com.github.nullptroma.wallenc.domain.interfaces package com.github.nullptroma.wallenc.domain.interfaces
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
import kotlinx.coroutines.flow.StateFlow
import java.time.Clock
import java.time.Instant import java.time.Instant
interface IStorageMetaInfo { interface IStorageMetaInfo {
val encInfo: StorageEncryptionInfo? val encInfo: StorageEncryptionInfo
val name: String? val name: String?
val lastModified: Instant val lastModified: Instant

View File

@@ -1,7 +1,7 @@
[versions] [versions]
agp = "8.8.0" agp = "8.8.0"
jacksonModuleKotlin = "2.18.2" jacksonModuleKotlin = "2.18.2"
kotlin = "2.0.10" kotlin = "2.0.20"
coreKtx = "1.15.0" coreKtx = "1.15.0"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.2.1" junitVersion = "1.2.1"
@@ -17,12 +17,11 @@ hiltNavigation = "1.2.0"
timber = "5.0.1" timber = "5.0.1"
yandexAuthSdk = "3.1.2" yandexAuthSdk = "3.1.2"
daggerHilt = "2.52" daggerHilt = "2.52"
ksp = "2.0.10-1.0.24" ksp = "2.0.20-1.0.25"
room = "2.6.1" room = "2.6.1"
retrofit = "2.11.0" retrofit = "2.11.0"
appcompat = "1.7.0" appcompat = "1.7.0"
material = "1.12.0" material = "1.12.0"
runtimeAndroid = "1.7.5"
[libraries] [libraries]
jackson-datatype-jsr310 = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" } jackson-datatype-jsr310 = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" }

View File

@@ -44,14 +44,14 @@ fun LocalVaultScreen(modifier: Modifier = Modifier,
detectTapGestures( detectTapGestures(
onTap = { _ -> viewModel.printStorageInfoToLog(it) } onTap = { _ -> viewModel.printStorageInfoToLog(it) }
) )
}) { }.padding(8.dp)) {
val available by it.isAvailable.collectAsStateWithLifecycle() val available by it.isAvailable.collectAsStateWithLifecycle()
val numOfFiles by it.numberOfFiles.collectAsStateWithLifecycle() val numOfFiles by it.numberOfFiles.collectAsStateWithLifecycle()
val size by it.size.collectAsStateWithLifecycle() val size by it.size.collectAsStateWithLifecycle()
val metaInfo by it.metaInfo.collectAsStateWithLifecycle() val metaInfo by it.metaInfo.collectAsStateWithLifecycle()
val enc = metaInfo.encInfo val enc = metaInfo.encInfo
Column { Column(modifier = Modifier.padding(4.dp)) {
Text(it.uuid.toString()) Text(it.uuid.toString())
Text("IsAvailable: $available") Text("IsAvailable: $available")
Text("Files: $numOfFiles") Text("Files: $numOfFiles")