diff --git a/app/src/androidTest/java/com/github/nullptroma/wallenc/app/integration/yandex/YandexDiskLiveIntegrationTest.kt b/app/src/androidTest/java/com/github/nullptroma/wallenc/app/integration/yandex/YandexDiskLiveIntegrationTest.kt index db5f7e1..a117d33 100644 --- a/app/src/androidTest/java/com/github/nullptroma/wallenc/app/integration/yandex/YandexDiskLiveIntegrationTest.kt +++ b/app/src/androidTest/java/com/github/nullptroma/wallenc/app/integration/yandex/YandexDiskLiveIntegrationTest.kt @@ -35,7 +35,7 @@ class YandexDiskLiveIntegrationTest { } @After - fun tearDown() = runBlocking { + fun tearDown(): Unit = runBlocking { runCatching { repository.delete(probePath, permanently = true) } } diff --git a/app/src/androidTest/java/com/github/nullptroma/wallenc/app/integration/yandex/YandexTestCredentials.kt b/app/src/androidTest/java/com/github/nullptroma/wallenc/app/integration/yandex/YandexTestCredentials.kt index 243386e..c079d20 100644 --- a/app/src/androidTest/java/com/github/nullptroma/wallenc/app/integration/yandex/YandexTestCredentials.kt +++ b/app/src/androidTest/java/com/github/nullptroma/wallenc/app/integration/yandex/YandexTestCredentials.kt @@ -7,12 +7,6 @@ object YandexTestCredentials { fun oauthToken(): String? = InstrumentationRegistry.getArguments().getString("yandex.oauth.token")?.takeIf { it.isNotBlank() } - fun userId(): String? = - InstrumentationRegistry.getArguments().getString("yandex.user.id")?.takeIf { it.isNotBlank() } - - fun vaultUuid(): String? = - InstrumentationRegistry.getArguments().getString("yandex.vault.uuid")?.takeIf { it.isNotBlank() } - fun assumePresent(message: String = "Добавьте yandex.test.oauth.token в local.properties") { assumeFalse(message, oauthToken().isNullOrBlank()) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5f545e8..13d8983 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -70,7 +70,8 @@ + android:foregroundServiceType="dataSync" + android:description="@string/fgs_task_pipeline_description" /> \ No newline at end of file diff --git a/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/app/UiStringModule.kt b/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/app/UiStringModule.kt index 0262bc4..909cd89 100644 --- a/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/app/UiStringModule.kt +++ b/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/app/UiStringModule.kt @@ -16,11 +16,19 @@ object UiStringModule { @Provides @Singleton fun provideUiStringResolver(@ApplicationContext context: Context): UiStringResolver = - UiStringResolver { id, args -> - if (args.isEmpty()) { - context.getString(id) - } else { - context.getString(id, *args) - } + object : UiStringResolver { + override fun invoke(id: Int, vararg formatArgs: Any): String = + if (formatArgs.isEmpty()) { + context.getString(id) + } else { + context.getString(id, *formatArgs) + } + + override fun plurals(id: Int, quantity: Int, vararg formatArgs: Any): String = + if (formatArgs.isEmpty()) { + context.resources.getQuantityString(id, quantity) + } else { + context.resources.getQuantityString(id, quantity, *formatArgs) + } } } diff --git a/app/src/main/java/com/github/nullptroma/wallenc/app/locale/AppLocaleStorage.kt b/app/src/main/java/com/github/nullptroma/wallenc/app/locale/AppLocaleStorage.kt index c3070bc..e5ad592 100644 --- a/app/src/main/java/com/github/nullptroma/wallenc/app/locale/AppLocaleStorage.kt +++ b/app/src/main/java/com/github/nullptroma/wallenc/app/locale/AppLocaleStorage.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking +import androidx.core.content.edit /** * Хранение выбранного языка. SharedPreferences — для синхронного чтения в @@ -50,7 +51,7 @@ internal object AppLocaleStorage { } fun persistLanguage(context: Context, language: AppLanguage) { - prefs(context).edit().putString(PREFS_KEY_LANGUAGE, language.storageValue).apply() + prefs(context).edit { putString(PREFS_KEY_LANGUAGE, language.storageValue) } applyLanguage(language) } @@ -83,7 +84,7 @@ internal object AppLocaleStorage { runCatching { val legacy = storage.legacyLocaleDataStore.data.first()[legacyLanguageKey] if (legacy != null) { - prefs(storage).edit().putString(PREFS_KEY_LANGUAGE, legacy).apply() + prefs(storage).edit { putString(PREFS_KEY_LANGUAGE, legacy) } } } } diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ebb78a4..cb610af 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -6,4 +6,5 @@ Подготовка… Выполняется… Отмена + Показывает прогресс фоновых задач хранилищ и vault. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0e544d6..c609598 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,4 +6,5 @@ Preparing… Running… Cancel + Shows progress while storage and vault tasks run in the background. diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/auth/YandexOAuthScopes.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/auth/YandexOAuthScopes.kt deleted file mode 100644 index b57fb63..0000000 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/auth/YandexOAuthScopes.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.github.nullptroma.wallenc.domain.vault.auth - -/** - * Scope-ы Яндекс.OAuth, которые нам нужны: только app_folder + disk.info. - * - * Используется как ссылка для синхронизации с консолью Yandex OAuth. - * Сам Yandex Auth SDK для WEBVIEW-логина запрашивает scope-ы, выставленные - * у приложения в OAuth-консоли; мы держим список здесь, чтобы было одно место правды. - */ -object YandexOAuthScopes { - const val DISK_APP_FOLDER = "cloud_api:disk.app_folder" - const val DISK_INFO = "cloud_api:disk.info" - - val ALL: Set = setOf(DISK_APP_FOLDER, DISK_INFO) -} diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/errors/VaultThrowableMapping.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/errors/VaultThrowableMapping.kt index ab7ca0c..3ddc50a 100644 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/errors/VaultThrowableMapping.kt +++ b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/errors/VaultThrowableMapping.kt @@ -9,13 +9,13 @@ import java.io.IOException fun Throwable.toVaultWallencException(): WallencException = when (this) { is WallencException -> this - is YandexDiskAuthException -> WallencException.Auth.Failed + is YandexDiskAuthException -> WallencException.Auth.Failed() is HttpException -> WallencException.Network.HttpFailed( operation = "http", statusCode = code(), cause = this, ) - is FileNotFoundException -> WallencException.Storage.FileNotFound + is FileNotFoundException -> WallencException.Storage.FileNotFound() is IOException -> mapVaultIo(this) is IllegalStateException -> mapIllegalState(this) is IllegalArgumentException -> mapIllegalArgument(this) @@ -26,29 +26,29 @@ private fun mapVaultIo(e: IOException): WallencException { val msg = e.message.orEmpty() return when { msg.contains("OAuth token is missing", ignoreCase = true) -> - WallencException.Auth.TokenMissing + WallencException.Auth.TokenMissing() msg.contains("HTTP 423", ignoreCase = true) || msg.contains("423 after retries", ignoreCase = true) -> - WallencException.Network.ResourceLocked + WallencException.Network.ResourceLocked() msg.contains("async operation timed out", ignoreCase = true) -> - WallencException.Network.OperationTimedOut + WallencException.Network.OperationTimedOut() msg.contains("async operation failed", ignoreCase = true) -> - WallencException.Network.OperationFailed + WallencException.Network.OperationFailed() else -> WallencException.Network.IoFailed(e) } } private fun mapIllegalState(e: IllegalStateException): WallencException = when { - e.message == "Not a file" -> WallencException.Storage.NotAFile - e.message == "Not a directory" -> WallencException.Storage.NotADirectory - e.message?.startsWith("Path segment is a file:") == true -> WallencException.Storage.PathIsFile + e.message == "Not a file" -> WallencException.Storage.NotAFile() + e.message == "Not a directory" -> WallencException.Storage.NotADirectory() + e.message?.startsWith("Path segment is a file:") == true -> WallencException.Storage.PathIsFile() e.message?.startsWith("Cannot openWrite over directory:") == true -> - WallencException.Storage.CannotWriteOverDirectory + WallencException.Storage.CannotWriteOverDirectory() e.message?.startsWith("Expected file after upload:") == true -> - WallencException.Storage.UnexpectedState - else -> WallencException.Storage.UnexpectedState + WallencException.Storage.UnexpectedState() + else -> WallencException.Storage.UnexpectedState() } private fun mapIllegalArgument(e: IllegalArgumentException): WallencException = when { - e.message == "Deleting root path is forbidden" -> WallencException.Storage.DeleteRootForbidden + e.message == "Deleting root path is forbidden" -> WallencException.Storage.DeleteRootForbidden() else -> WallencException.Unknown(e) } diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskApi.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskApi.kt index f203fe0..40734ea 100644 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskApi.kt +++ b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskApi.kt @@ -12,7 +12,6 @@ import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.Headers import retrofit2.http.PATCH -import retrofit2.http.POST import retrofit2.http.PUT import retrofit2.http.Query import retrofit2.http.Url @@ -46,13 +45,6 @@ interface YandexDiskApi { @Query("permanently") permanently: Boolean, ): Response - @POST("v1/disk/resources/move") - suspend fun moveResource( - @Query("from") from: String, - @Query("path") toPath: String, - @Query("overwrite") overwrite: Boolean = false, - ): Response - @GET("v1/disk/resources/upload") suspend fun getUploadLink( @Query("path") path: String, diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskApiFactory.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskApiFactory.kt index 45c6fa9..65e4192 100644 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskApiFactory.kt +++ b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskApiFactory.kt @@ -33,7 +33,7 @@ class YandexDiskApiFactory( /** * [tokenProvider] вызывается на каждый HTTP-запрос к cloud-api (свежий токен из БД). */ - fun createAuthenticatedApi(tokenProvider: () -> String?): com.github.nullptroma.wallenc.domain.vault.network.yandexdisk.YandexDiskApi { + fun createAuthenticatedApi(tokenProvider: () -> String?): YandexDiskApi { val client = OkHttpClient.Builder() .addInterceptor { chain -> val token = tokenProvider() diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/dto/YandexDiskDto.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/dto/YandexDiskDto.kt index 53450eb..4e2b42f 100644 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/dto/YandexDiskDto.kt +++ b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/dto/YandexDiskDto.kt @@ -47,13 +47,6 @@ data class OperationStatusDto( val status: String? = null, ) -@JsonIgnoreProperties(ignoreUnknown = true) -data class ApiErrorDto( - val message: String? = null, - val description: String? = null, - val error: String? = null, -) - @JsonIgnoreProperties(ignoreUnknown = true) data class CustomPropertiesPatchDto( @param:JsonProperty("custom_properties") val customProperties: Map, diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/UnlockManager.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/UnlockManager.kt index 452f146..cee2386 100644 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/UnlockManager.kt +++ b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/UnlockManager.kt @@ -110,9 +110,9 @@ class UnlockManager( rememberPassword: Boolean ): EncryptedStorage = withContext(ioDispatcher) { return@withContext mutex.withLock { - val encInfo = storage.metaInfo.value.encInfo ?: throw WallencException.Storage.EncInfoMissing + val encInfo = storage.metaInfo.value.encInfo ?: throw WallencException.Storage.EncInfoMissing() if (!Encryptor.checkKey(key, encInfo)) - throw WallencException.Storage.IncorrectKey + throw WallencException.Storage.IncorrectKey() val opened = _openedStorages.value.toMutableMap() val cur = opened[storage.uuid] diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/encrypt/EncryptedStorage.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/encrypt/EncryptedStorage.kt index 1912174..d761522 100644 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/encrypt/EncryptedStorage.kt +++ b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/encrypt/EncryptedStorage.kt @@ -29,7 +29,7 @@ class EncryptedStorage private constructor( private val scope = CoroutineScope(ioDispatcher + job) private val encInfo = - source.metaInfo.value.encInfo ?: throw WallencException.Storage.NotEncrypted + source.metaInfo.value.encInfo ?: throw WallencException.Storage.NotEncrypted() override val isVirtualStorage: Boolean = true @@ -52,7 +52,7 @@ class EncryptedStorage private constructor( private fun checkKey() { if (!Encryptor.checkKey(key, encInfo)) - throw WallencException.Storage.IncorrectKey + throw WallencException.Storage.IncorrectKey() } fun getKey(): EncryptKey = EncryptKey(key.bytes) diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/encrypt/EncryptedStorageAccessor.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/encrypt/EncryptedStorageAccessor.kt index 8b8b78d..fdeb83c 100644 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/encrypt/EncryptedStorageAccessor.kt +++ b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/encrypt/EncryptedStorageAccessor.kt @@ -128,18 +128,10 @@ class EncryptedStorageAccessor( return source.getFiles(encryptPath(systemHiddenDirName)) } - private fun encryptEntity(file: IFile): IFile { - return CommonFile(encryptMeta(file.metaInfo)) - } - private fun decryptEntity(file: IFile): IFile { return CommonFile(decryptMeta(file.metaInfo)) } - private fun encryptEntity(dir: IDirectory): IDirectory { - return CommonDirectory(encryptMeta(dir.metaInfo), dir.elementsCount) - } - private fun decryptEntity(dir: IDirectory): IDirectory { return CommonDirectory(decryptMeta(dir.metaInfo), dir.elementsCount) } @@ -303,12 +295,11 @@ class EncryptedStorageAccessor( return emptyList() } return runCatching { - val javaType = jackson.typeFactory.constructCollectionType( + val journalType = jackson.typeFactory.constructCollectionType( List::class.java, StorageSyncJournalEntry::class.java, ) - @Suppress("UNCHECKED_CAST") - (jackson.readValue(bytes, javaType) as List) + jackson.readValue>(bytes, journalType) }.getOrElse { emptyList() } diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/local/LocalStorageAccessor.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/local/LocalStorageAccessor.kt index a5f7b6b..d7f0eed 100644 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/local/LocalStorageAccessor.kt +++ b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/local/LocalStorageAccessor.kt @@ -214,10 +214,6 @@ class LocalStorageAccessor( val filePath = Path(filesystemBasePath.pathString, storagePath) return from(filesystemBasePath, filePath.toFile()) } - - fun from(filesystemBasePath: Path, meta: IMetaInfo): LocalStorageFilePair? { - return from(filesystemBasePath, meta.path) - } } } @@ -232,7 +228,7 @@ class LocalStorageAccessor( dirCallback: (suspend (File, CommonDirectory) -> Unit)? = null ) { if (!checkAvailable()) - throw WallencException.Storage.NotAvailable + throw WallencException.Storage.NotAvailable() val basePath = Path(_filesystemBasePath.pathString, baseStoragePath) val workedFiles = mutableSetOf() val workedMetaFiles = mutableSetOf() @@ -398,7 +394,7 @@ class LocalStorageAccessor( override suspend fun getFileInfo(path: String): IFile { val pair = LocalStorageFilePair.from(_filesystemBasePath, path) - ?: throw WallencException.Storage.UnexpectedState + ?: throw WallencException.Storage.UnexpectedState() return CommonFile( metaInfo = pair.meta, ) @@ -406,7 +402,7 @@ class LocalStorageAccessor( override suspend fun getDirInfo(path: String): IDirectory { val pair = LocalStorageFilePair.from(_filesystemBasePath, path) - ?: throw WallencException.Storage.UnexpectedState + ?: throw WallencException.Storage.UnexpectedState() return CommonDirectory( metaInfo = pair.meta, elementsCount = null @@ -415,7 +411,7 @@ class LocalStorageAccessor( override suspend fun setHidden(path: String, hidden: Boolean) { val pair = LocalStorageFilePair.from(_filesystemBasePath, path) - ?: throw WallencException.Storage.UnexpectedState + ?: throw WallencException.Storage.UnexpectedState() if (pair.meta.isHidden == hidden) return val newMeta = pair.meta.copy(isHidden = hidden) @@ -442,7 +438,7 @@ class LocalStorageAccessor( val path = Path(_filesystemBasePath.pathString, storagePath) val file = path.toFile() if (file.exists() && file.isDirectory) { - throw WallencException.Storage.UnexpectedState + throw WallencException.Storage.UnexpectedState() } else if(!file.exists()) { val parent = Path(storagePath).parent createDir(parent.pathString) @@ -453,7 +449,7 @@ class LocalStorageAccessor( } val pair = LocalStorageFilePair.from(_filesystemBasePath, file) - ?: throw WallencException.Storage.UnexpectedState + ?: throw WallencException.Storage.UnexpectedState() val newMeta = pair.meta.copy(lastModified = Clock.systemUTC().instant(), size = Files.size(pair.file.toPath())) writeMeta(pair.metaFile, newMeta) _filesUpdates.emit( @@ -470,13 +466,13 @@ class LocalStorageAccessor( val path = Path(_filesystemBasePath.pathString, storagePath) val file = path.toFile() if (file.exists() && !file.isDirectory) { - throw WallencException.Storage.UnexpectedState + throw WallencException.Storage.UnexpectedState() } else if(!file.exists()) { Files.createDirectories(path) } val pair = LocalStorageFilePair.from(_filesystemBasePath, file) - ?: throw WallencException.Storage.UnexpectedState + ?: throw WallencException.Storage.UnexpectedState() val newMeta = pair.meta.copy(lastModified = Clock.systemUTC().instant()) writeMeta(pair.metaFile, newMeta) _dirsUpdates.emit( @@ -514,7 +510,7 @@ class LocalStorageAccessor( override suspend fun delete(path: String) = withContext(ioDispatcher) { if (path == "/" || path.isBlank()) { - throw WallencException.Storage.DeleteRootForbidden + throw WallencException.Storage.DeleteRootForbidden() } val pair = LocalStorageFilePair.from(_filesystemBasePath, path) if (pair != null) { @@ -529,7 +525,7 @@ class LocalStorageAccessor( override suspend fun openWrite(path: String): OutputStream = withContext(ioDispatcher) { touchFileInternal(path, recordJournal = false) val pair = LocalStorageFilePair.from(_filesystemBasePath, path) - ?: throw WallencException.Storage.FileNotFound + ?: throw WallencException.Storage.FileNotFound() return@withContext pair.file.outputStream().onClosed { CoroutineScope(ioDispatcher).launch { touchFileInternal(path, recordJournal = false) @@ -541,13 +537,13 @@ class LocalStorageAccessor( override suspend fun openRead(path: String): InputStream = withContext(ioDispatcher) { val pair = LocalStorageFilePair.from(_filesystemBasePath, path) - ?: throw WallencException.Storage.FileNotFound + ?: throw WallencException.Storage.FileNotFound() return@withContext pair.file.inputStream() } override suspend fun moveToTrash(path: String) = withContext(ioDispatcher) { val pair = LocalStorageFilePair.from(_filesystemBasePath, path) - ?: throw WallencException.Storage.FileNotFound + ?: throw WallencException.Storage.FileNotFound() val newMeta = pair.meta.copy(isDeleted = true) writeMeta(pair.metaFile, newMeta) appendSyncEntry(path = path, operation = StorageSyncOperation.DELETE) diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/yandex/YandexStorageAccessor.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/yandex/YandexStorageAccessor.kt index d939fdc..d769e85 100644 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/yandex/YandexStorageAccessor.kt +++ b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/storages/yandex/YandexStorageAccessor.kt @@ -107,7 +107,7 @@ class YandexStorageAccessor( } catch (e: YandexDiskAuthException) { reportAuthFailure() _storageReady.value = false - throw WallencException.Auth.Failed + throw WallencException.Auth.Failed() } catch (e: Exception) { _storageReady.value = false throw e.toVaultWallencException() @@ -121,7 +121,7 @@ class YandexStorageAccessor( throw e } catch (e: YandexDiskAuthException) { reportAuthFailure() - throw WallencException.Auth.Failed + throw WallencException.Auth.Failed() } catch (e: Throwable) { throw e.toVaultWallencException() } @@ -176,9 +176,7 @@ class YandexStorageAccessor( systemDirEnsured = true } - private fun statsFileRel(): String = "/$SYSTEM_HIDDEN_DIRNAME/$STATS_FILENAME" - - private fun statsDiskPath(): String = toDiskPath(statsFileRel()) + private fun statsDiskPath(): String = toDiskPath("/$SYSTEM_HIDDEN_DIRNAME/$STATS_FILENAME") private suspend fun readPersistedStats(): YandexVaultPersistedStats? { val meta = guard { repo.getOrNull(statsDiskPath()) } ?: return null @@ -452,13 +450,13 @@ class YandexStorageAccessor( override suspend fun getFileInfo(path: String): IFile = withContext(ioDispatcher) { val r = guard { repo.get(toDiskPath(path)) } - if (r.type != "file") throw WallencException.Storage.NotAFile + if (r.type != "file") throw WallencException.Storage.NotAFile() r.toCommonFile(path) } override suspend fun getDirInfo(path: String): IDirectory = withContext(ioDispatcher) { val r = guard { repo.get(toDiskPath(path)) } - if (r.type != "dir") throw WallencException.Storage.NotADirectory + if (r.type != "dir") throw WallencException.Storage.NotADirectory() r.toCommonDir(path) } @@ -497,7 +495,7 @@ class YandexStorageAccessor( val diskPath = toDiskPath(acc) when (guard { repo.getOrNull(diskPath) }?.type) { "dir" -> continue - "file" -> throw WallencException.Storage.PathIsFile + "file" -> throw WallencException.Storage.PathIsFile() else -> guard { repo.createFolder(diskPath) } } } @@ -505,7 +503,7 @@ class YandexStorageAccessor( override suspend fun delete(path: String) = withContext(ioDispatcher) { if (path == "/" || path.isBlank()) { - throw WallencException.Storage.DeleteRootForbidden + throw WallencException.Storage.DeleteRootForbidden() } val diskPath = toDiskPath(path) val prior = guard { repo.getOrNull(diskPath) } @@ -538,14 +536,14 @@ class YandexStorageAccessor( val diskPath = toDiskPath(path) val prior = guard { repo.getOrNull(diskPath) } if (prior?.type == "dir") { - throw WallencException.Storage.CannotWriteOverDirectory + throw WallencException.Storage.CannotWriteOverDirectory() } val hadFile = prior?.type == "file" val priorSize = if (prior?.type == "file") prior.size ?: 0L else 0L guard { repo.uploadFile(diskPath, tmp, overwrite = true) } val after = guard { getMetadataAfterWrite(diskPath) } if (after.type != "file") { - throw WallencException.Storage.UnexpectedState + throw WallencException.Storage.UnexpectedState() } val newSize = after.size ?: 0L _size.value = ((_size.value ?: 0L) + newSize - priorSize).coerceAtLeast(0L) @@ -605,12 +603,11 @@ class YandexStorageAccessor( return@withContext emptyList() } return@withContext runCatching { - val javaType = statsMapper.typeFactory.constructCollectionType( + val journalType = statsMapper.typeFactory.constructCollectionType( List::class.java, StorageSyncJournalEntry::class.java, ) - @Suppress("UNCHECKED_CAST") - (statsMapper.readValue(bytes, javaType) as List) + statsMapper.readValue>(bytes, journalType) }.getOrElse { emptyList() } diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/utils/CloseHandledStream.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/utils/CloseHandledStream.kt index 58af5aa..a78c5fe 100644 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/utils/CloseHandledStream.kt +++ b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/utils/CloseHandledStream.kt @@ -89,16 +89,8 @@ class CloseHandledStreamExtension { return CloseHandledOutputStream(this, {}, callback) } - fun InputStream.onClosed(callback: ()->Unit): InputStream { - return CloseHandledInputStream(this, {}, callback) - } - fun OutputStream.onClosing(callback: ()->Unit): OutputStream { return CloseHandledOutputStream(this, callback) {} } - - fun InputStream.onClosing(callback: ()->Unit): InputStream { - return CloseHandledInputStream(this, callback) {} - } } } \ No newline at end of file diff --git a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/vaults/local/LocalVault.kt b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/vaults/local/LocalVault.kt index 2629d1e..4aaef56 100644 --- a/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/vaults/local/LocalVault.kt +++ b/domain-vault/src/main/java/com/github/nullptroma/wallenc/domain/vault/vaults/local/LocalVault.kt @@ -66,7 +66,7 @@ class LocalVault( private suspend fun readStorages() { val path = path.value if (path == null || !_isAvailable.value) { - throw WallencException.Storage.NotAvailable + throw WallencException.Storage.NotAvailable() } val dirs = path.listFiles()?.filter { it.isDirectory } @@ -81,7 +81,7 @@ class LocalVault( override suspend fun createStorage(): LocalStorage = withContext(ioDispatcher) { val path = path.value if (path == null || !_isAvailable.value) { - throw WallencException.Storage.NotAvailable + throw WallencException.Storage.NotAvailable() } val storageUuid = UUID.randomUUID() @@ -106,7 +106,7 @@ class LocalVault( override suspend fun remove(storage: IStorage) = withContext(ioDispatcher) { val path = path.value if (path == null || !_isAvailable.value) { - throw WallencException.Storage.NotAvailable + throw WallencException.Storage.NotAvailable() } val curStorages = _storages.value.toMutableList() diff --git a/domain-vault/src/test/java/com/github/nullptroma/wallenc/domain/vault/errors/VaultThrowableMappingTest.kt b/domain-vault/src/test/java/com/github/nullptroma/wallenc/domain/vault/errors/VaultThrowableMappingTest.kt index 5b8fbfd..268fb1d 100644 --- a/domain-vault/src/test/java/com/github/nullptroma/wallenc/domain/vault/errors/VaultThrowableMappingTest.kt +++ b/domain-vault/src/test/java/com/github/nullptroma/wallenc/domain/vault/errors/VaultThrowableMappingTest.kt @@ -16,7 +16,7 @@ class VaultThrowableMappingTest { @Test fun mapsYandexDiskAuthToAuthFailed() { val mapped = YandexDiskAuthException("unauthorized").toVaultWallencException() - assertEquals(WallencException.Auth.Failed, mapped) + assertTrue(mapped is WallencException.Auth.Failed) } @Test @@ -30,18 +30,18 @@ class VaultThrowableMappingTest { @Test fun mapsMissingOAuthTokenIoToTokenMissing() { val mapped = IOException("Yandex OAuth token is missing").toVaultWallencException() - assertEquals(WallencException.Auth.TokenMissing, mapped) + assertTrue(mapped is WallencException.Auth.TokenMissing) } @Test fun mapsFileNotFoundToStorageFileNotFound() { val mapped = FileNotFoundException("x").toVaultWallencException() - assertEquals(WallencException.Storage.FileNotFound, mapped) + assertTrue(mapped is WallencException.Storage.FileNotFound) } @Test fun mapsIllegalStateNotAFile() { val mapped = IllegalStateException("Not a file").toVaultWallencException() - assertEquals(WallencException.Storage.NotAFile, mapped) + assertTrue(mapped is WallencException.Storage.NotAFile) } } diff --git a/domain-vault/src/test/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskRepositoryTestFactory.kt b/domain-vault/src/test/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskRepositoryTestFactory.kt index 8c6ccdf..cf79e3d 100644 --- a/domain-vault/src/test/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskRepositoryTestFactory.kt +++ b/domain-vault/src/test/java/com/github/nullptroma/wallenc/domain/vault/network/yandexdisk/YandexDiskRepositoryTestFactory.kt @@ -1,9 +1,6 @@ package com.github.nullptroma.wallenc.domain.vault.network.yandexdisk import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.github.nullptroma.wallenc.domain.vault.network.yandexdisk.repository.YandexDiskRepository -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers import okhttp3.OkHttpClient import retrofit2.Retrofit import retrofit2.converter.jackson.JacksonConverterFactory @@ -13,16 +10,6 @@ object YandexDiskRepositoryTestFactory { private val jackson = jacksonObjectMapper().findAndRegisterModules() - fun create( - baseUrl: String, - oauthToken: String, - ioDispatcher: CoroutineDispatcher = Dispatchers.IO, - rawHttp: OkHttpClient = OkHttpClient(), - ): YandexDiskRepository { - val api = createApi(baseUrl) { oauthToken } - return YandexDiskRepository(api, rawHttp, ioDispatcher) - } - fun createApi( baseUrl: String, tokenProvider: () -> String?, diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptorWithStaticIv.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptorWithStaticIv.kt index 22c46fe..b893609 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptorWithStaticIv.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptorWithStaticIv.kt @@ -2,11 +2,7 @@ package com.github.nullptroma.wallenc.domain.encrypt import com.github.nullptroma.wallenc.domain.encrypt.Encryptor.Companion.AES_SETTINGS import kotlinx.coroutines.DisposableHandle -import java.io.InputStream -import java.io.OutputStream import javax.crypto.Cipher -import javax.crypto.CipherInputStream -import javax.crypto.CipherOutputStream import javax.crypto.SecretKey import javax.crypto.spec.IvParameterSpec import kotlin.io.encoding.Base64 @@ -47,22 +43,6 @@ class EncryptorWithStaticIv(private var secretKey: SecretKey, iv: ByteArray) : D return decryptedBytes } - fun encryptStream(stream: OutputStream): OutputStream { - if(secretKey.isDestroyed) - throw Exception("Object was destroyed") - val cipher = Cipher.getInstance(AES_SETTINGS) - cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec) // инициализация шифратора - return CipherOutputStream(stream, cipher) - } - - fun decryptStream(stream: InputStream): InputStream { - if(secretKey.isDestroyed) - throw Exception("Object was destroyed") - val cipher = Cipher.getInstance(AES_SETTINGS) - cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec) - return CipherInputStream(stream, cipher) - } - override fun dispose() { secretKey.destroy() } diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/errors/WallencException.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/errors/WallencException.kt index 6109d75..5e3fef5 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/errors/WallencException.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/errors/WallencException.kt @@ -10,45 +10,44 @@ sealed class WallencException( ) : Exception(message, cause) { sealed class Feature : WallencException() { - data object StorageNotFound : Feature() - data object NeedsDecryptedView : Feature() - data object SecretNotFound : Feature() - data object StorageNotWritable : Feature() + class StorageNotFound : Feature() + class NeedsDecryptedView : Feature() + class SecretNotFound : Feature() + class StorageNotWritable : Feature() } sealed class Storage(cause: Throwable? = null) : WallencException(cause = cause) { - data object NotAvailable : Storage() - data object FileNotFound : Storage() - data object IncorrectKey : Storage() + class NotAvailable : Storage() + class FileNotFound : Storage() + class IncorrectKey : Storage() + class EncInfoMissing : Storage() + class NotEncrypted : Storage() + class NotWritable : Storage() + class NotAFile : Storage() + class NotADirectory : Storage() + class PathIsFile : Storage() + class CannotWriteOverDirectory : Storage() + class DeleteRootForbidden : Storage() + class UnexpectedState : Storage() data class IoFailed(override val cause: Throwable) : Storage(cause) - data object EncInfoMissing : Storage() - data object NotEncrypted : Storage() - data object NotWritable : Storage() - data object NotAFile : Storage() - data object NotADirectory : Storage() - data object PathIsFile : Storage() - data object CannotWriteOverDirectory : Storage() - data object DeleteRootForbidden : Storage() - data object UnexpectedState : Storage() } - /** Ошибки аутентификации (OAuth, токен), без привязки к провайдеру. */ sealed class Auth : WallencException() { - data object Failed : Auth() - data object TokenMissing : Auth() + class Failed : Auth() + class TokenMissing : Auth() } - /** Сетевые и удалённые операции (HTTP, блокировки, таймауты). */ sealed class Network(cause: Throwable? = null) : WallencException(cause = cause) { data class HttpFailed( val operation: String, val statusCode: Int, override val cause: Throwable? = null, ) : Network(cause) + data class IoFailed(override val cause: Throwable) : Network(cause) - data object ResourceLocked : Network() - data object OperationFailed : Network() - data object OperationTimedOut : Network() + class ResourceLocked : Network() + class OperationFailed : Network() + class OperationTimedOut : Network() } data class Unknown(override val cause: Throwable?) : WallencException(cause = cause) diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/errors/WallencExceptionMapping.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/errors/WallencExceptionMapping.kt index 9036181..0a03c3b 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/errors/WallencExceptionMapping.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/errors/WallencExceptionMapping.kt @@ -5,9 +5,7 @@ import java.io.IOException fun Throwable.toWallencException(): WallencException = when (this) { is WallencException -> this - is FileNotFoundException -> WallencException.Storage.FileNotFound + is FileNotFoundException -> WallencException.Storage.FileNotFound() is IOException -> WallencException.Network.IoFailed(this) else -> WallencException.Unknown(this) } - -fun Throwable.rethrowAsWallencException(): Nothing = throw toWallencException() diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IStorageExplorer.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IStorageExplorer.kt deleted file mode 100644 index 83fa9c1..0000000 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IStorageExplorer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.nullptroma.wallenc.domain.interfaces - -import kotlinx.coroutines.flow.StateFlow - -interface IStorageExplorer { - val currentPath: StateFlow - - // TODO - // пока бесполезный интерфейс -} \ No newline at end of file diff --git a/domain/src/test/java/com/github/nullptroma/wallenc/domain/errors/WallencExceptionMappingTest.kt b/domain/src/test/java/com/github/nullptroma/wallenc/domain/errors/WallencExceptionMappingTest.kt index 7f19b61..03915ce 100644 --- a/domain/src/test/java/com/github/nullptroma/wallenc/domain/errors/WallencExceptionMappingTest.kt +++ b/domain/src/test/java/com/github/nullptroma/wallenc/domain/errors/WallencExceptionMappingTest.kt @@ -1,6 +1,5 @@ package com.github.nullptroma.wallenc.domain.errors -import org.junit.Assert.assertEquals import org.junit.Assert.assertSame import org.junit.Assert.assertTrue import org.junit.Test @@ -11,14 +10,14 @@ class WallencExceptionMappingTest { @Test fun preservesWallencException() { - val original = WallencException.Feature.StorageNotFound + val original = WallencException.Feature.StorageNotFound() assertSame(original, original.toWallencException()) } @Test fun mapsFileNotFoundException() { val mapped = FileNotFoundException("missing").toWallencException() - assertEquals(WallencException.Storage.FileNotFound, mapped) + assertTrue(mapped is WallencException.Storage.FileNotFound) } @Test diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 247ebfe..c176d2d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] -agp = "9.1.1" +agp = "9.2.1" jacksonModuleKotlin = "2.21.3" +javaxInject = "1" kotlin = "2.3.21" coreKtx = "1.18.0" junit = "4.13.2" @@ -32,11 +33,11 @@ datastore = "1.2.1" mockk = "1.14.9" robolectric = "4.16.1" androidxArchCore = "2.2.0" -turbine = "1.2.1" [libraries] jackson-datatype-jsr310 = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" } jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jacksonModuleKotlin" } +javax-inject = { module = "javax.inject:javax.inject", version.ref = "javaxInject" } kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlinReflect" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesCore" } @@ -45,7 +46,6 @@ mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = " room-testing = { group = "androidx.room", name = "room-testing", version.ref = "room" } robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } androidx-arch-core-testing = { group = "androidx.arch.core", name = "core-testing", version.ref = "androidxArchCore" } -turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" } androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigation" } diff --git a/infrastructure-android/src/main/java/com/github/nullptroma/wallenc/infrastructure/android/db/app/repository/StorageMetaInfoRepository.kt b/infrastructure-android/src/main/java/com/github/nullptroma/wallenc/infrastructure/android/db/app/repository/StorageMetaInfoRepository.kt deleted file mode 100644 index 87af41e..0000000 --- a/infrastructure-android/src/main/java/com/github/nullptroma/wallenc/infrastructure/android/db/app/repository/StorageMetaInfoRepository.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.github.nullptroma.wallenc.infrastructure.android.db.app.repository - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.github.nullptroma.wallenc.infrastructure.android.db.app.dao.StorageMetaInfoDao -import com.github.nullptroma.wallenc.infrastructure.android.db.app.model.DbStorageMetaInfo -import com.github.nullptroma.wallenc.domain.vault.utils.IProvider -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 { - 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)) - } - - suspend fun delete(uuid: UUID) = withContext(ioDispatcher) { - dao.delete(uuid) - } - - - fun createSingleStorageProvider(uuid: UUID): SingleStorageMetaInfoProvider { - return SingleStorageMetaInfoProvider(this, uuid) - } - - class SingleStorageMetaInfoProvider ( - private val repo: StorageMetaInfoRepository, - val uuid: UUID - ) : IProvider { - override suspend fun get(): CommonStorageMetaInfo? = repo.getMeta(uuid) - override suspend fun clear() = repo.delete(uuid) - override suspend fun set(value: CommonStorageMetaInfo) = repo.setMeta(uuid, value) - } - - companion object { - private val jackson = jacksonObjectMapper().apply { findAndRegisterModules() } - } -} diff --git a/task-runtime/src/test/java/com/github/nullptroma/wallenc/task/runtime/TaskOrchestratorTest.kt b/task-runtime/src/test/java/com/github/nullptroma/wallenc/task/runtime/TaskOrchestratorTest.kt index f41f87c..12d59b9 100644 --- a/task-runtime/src/test/java/com/github/nullptroma/wallenc/task/runtime/TaskOrchestratorTest.kt +++ b/task-runtime/src/test/java/com/github/nullptroma/wallenc/task/runtime/TaskOrchestratorTest.kt @@ -2,7 +2,6 @@ package com.github.nullptroma.wallenc.task.runtime import com.github.nullptroma.wallenc.domain.errors.WallencException import com.github.nullptroma.wallenc.domain.tasks.TaskLogLevel -import com.github.nullptroma.wallenc.domain.tasks.TaskProgress import com.github.nullptroma.wallenc.domain.tasks.TaskProgressLabel import com.github.nullptroma.wallenc.domain.tasks.TaskRunState import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -10,7 +9,6 @@ import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test @@ -59,13 +57,13 @@ class TaskOrchestratorTest { title = "Fail", dispatcher = dispatcher, work = { ctx -> - ctx.fail(WallencException.Storage.FileNotFound) + ctx.fail(WallencException.Storage.FileNotFound()) }, ) advanceUntilIdle() val task = orchestrator.pipelineState.value.tasks.first { it.id == id } val failed = task.state as TaskRunState.Failed - assertEquals(WallencException.Storage.FileNotFound, failed.error) + assertTrue(failed.error is WallencException.Storage.FileNotFound) } @Test diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/WallencViewModel.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/WallencViewModel.kt index be6bbd0..3eb7760 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/WallencViewModel.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/WallencViewModel.kt @@ -4,13 +4,11 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi import androidx.lifecycle.viewmodel.compose.saveable -import com.github.nullptroma.wallenc.ui.screens.ScreenRoute import com.github.nullptroma.wallenc.ui.screens.main.MainRoute import com.github.nullptroma.wallenc.ui.screens.main.screens.tasks.TaskPipelineRoute import com.github.nullptroma.wallenc.ui.screens.settings.SettingsRoute import com.github.nullptroma.wallenc.ui.screens.sync.StorageSyncRoute import dagger.hilt.android.lifecycle.HiltViewModel -import kotlin.collections.set @HiltViewModel class WallencViewModel @javax.inject.Inject constructor(savedStateHandle: SavedStateHandle) : @@ -27,10 +25,4 @@ class WallencViewModel @javax.inject.Inject constructor(savedStateHandle: SavedS ) } private set - - fun updateRoute(qualifiedName: String, route: ScreenRoute) { - routes = routes.toMutableMap().apply { - this[qualifiedName] = route - } - } } \ No newline at end of file diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/QrScannerDialog.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/QrScannerDialog.kt index 6038762..00e63b5 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/QrScannerDialog.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/QrScannerDialog.kt @@ -1,8 +1,8 @@ package com.github.nullptroma.wallenc.ui.elements -import android.annotation.SuppressLint import android.view.ViewGroup import androidx.camera.core.CameraSelector +import androidx.camera.core.ExperimentalGetImage import androidx.camera.core.ImageAnalysis import androidx.camera.core.Preview import androidx.camera.lifecycle.ProcessCameraProvider @@ -28,11 +28,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView -import androidx.lifecycle.compose.LocalLifecycleOwner import com.github.nullptroma.wallenc.ui.R import com.google.mlkit.vision.barcode.BarcodeScannerOptions import com.google.mlkit.vision.barcode.BarcodeScanning @@ -41,8 +39,8 @@ import com.google.mlkit.vision.common.InputImage import java.util.concurrent.Executors import java.util.concurrent.atomic.AtomicBoolean -@OptIn(ExperimentalMaterial3Api::class) -@SuppressLint("UnsafeOptInUsageError") +@ExperimentalGetImage +@OptIn(ExperimentalMaterial3Api::class, ExperimentalGetImage::class) @Composable fun QrScannerDialog( onDismiss: () -> Unit, diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/indication/ScaleIndication.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/indication/ScaleIndication.kt deleted file mode 100644 index ed7d1fe..0000000 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/indication/ScaleIndication.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.github.nullptroma.wallenc.ui.elements.indication - -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.spring -import androidx.compose.foundation.IndicationNodeFactory -import androidx.compose.foundation.interaction.InteractionSource -import androidx.compose.foundation.interaction.PressInteraction -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.drawscope.ContentDrawScope -import androidx.compose.ui.graphics.drawscope.scale -import androidx.compose.ui.node.DelegatableNode -import androidx.compose.ui.node.DrawModifierNode -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch - -private class ScaleNode(private val interactionSource: InteractionSource) : - Modifier.Node(), DrawModifierNode { - - var currentPressPosition: Offset = Offset.Zero - val animatedScalePercent = Animatable(1f) - - private suspend fun animateToPressed(pressPosition: Offset) { - currentPressPosition = pressPosition - animatedScalePercent.animateTo(0.9f, spring()) - } - - private suspend fun animateToResting() { - animatedScalePercent.animateTo(1f, spring()) - } - - override fun onAttach() { - coroutineScope.launch { - interactionSource.interactions.collectLatest { interaction -> - when (interaction) { - is PressInteraction.Press -> animateToPressed(interaction.pressPosition) - is PressInteraction.Release -> animateToResting() - is PressInteraction.Cancel -> animateToResting() - } - } - } - } - - override fun ContentDrawScope.draw() { - scale( - scale = animatedScalePercent.value, - pivot = currentPressPosition - ) { - this@draw.drawContent() - } - } -} - -object ScaleIndication : IndicationNodeFactory { - override fun create(interactionSource: InteractionSource): DelegatableNode { - return ScaleNode(interactionSource) - } - - override fun equals(other: Any?): Boolean = other === ScaleIndication - override fun hashCode() = 100 -} \ No newline at end of file diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/extensions/Modifier.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/extensions/Modifier.kt deleted file mode 100644 index 0e21ff9..0000000 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/extensions/Modifier.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.github.nullptroma.wallenc.ui.extensions - -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.pointer.PointerEventPass -import androidx.compose.ui.input.pointer.PointerInputChange -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.layout.layout -import androidx.compose.ui.unit.Dp - -fun Modifier.ignoreHorizontalParentPadding(horizontal: Dp): Modifier { - return this.layout { measurable, constraints -> - val overrideWidth = constraints.maxWidth + 2 * horizontal.roundToPx() - val placeable = measurable.measure(constraints.copy(maxWidth = overrideWidth)) - layout(placeable.width, placeable.height) { - placeable.place(0, 0) - } - } -} - -fun Modifier.ignoreVerticalParentPadding(vertical: Dp): Modifier { - return this.layout { measurable, constraints -> - val overrideHeight = constraints.maxHeight + 2 * vertical.roundToPx() - val placeable = measurable.measure(constraints.copy(maxHeight = overrideHeight)) - layout(placeable.width, placeable.height) { - placeable.place(0, 0) - } - } -} - -fun Modifier.gesturesDisabled(disabled: Boolean = true) = - if (disabled) { - pointerInput(Unit) { - awaitPointerEventScope { - // we should wait for all new pointer events - while (true) { - awaitPointerEvent(pass = PointerEventPass.Initial) - .changes - .forEach(PointerInputChange::consume) - } - } - } - } else { - this - } \ No newline at end of file diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/AppStrings.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/AppStrings.kt index 84d3393..7178ad7 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/AppStrings.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/AppStrings.kt @@ -6,50 +6,50 @@ import com.github.nullptroma.wallenc.usecases.AddStorageToSyncGroupResult import com.github.nullptroma.wallenc.vault.contract.VaultLinkFailure fun WallencException.toUserNotification(): UserNotification.TextRes = when (this) { - WallencException.Feature.StorageNotFound -> + is WallencException.Feature.StorageNotFound -> UserNotification.TextRes(R.string.error_storage_not_found) - WallencException.Feature.NeedsDecryptedView -> + is WallencException.Feature.NeedsDecryptedView -> UserNotification.TextRes(R.string.error_storage_locked_view) - WallencException.Feature.SecretNotFound -> + is WallencException.Feature.SecretNotFound -> UserNotification.TextRes(R.string.error_secret_not_found) - WallencException.Feature.StorageNotWritable -> + is WallencException.Feature.StorageNotWritable -> UserNotification.TextRes(R.string.error_storage_not_writable) - WallencException.Storage.NotAvailable, - WallencException.Storage.NotWritable, + is WallencException.Storage.NotAvailable, + is WallencException.Storage.NotWritable, -> UserNotification.TextRes(R.string.error_storage_not_writable) - WallencException.Storage.FileNotFound -> + is WallencException.Storage.FileNotFound -> UserNotification.TextRes(R.string.error_file_not_found) - WallencException.Storage.IncorrectKey -> + is WallencException.Storage.IncorrectKey -> UserNotification.TextRes(R.string.error_incorrect_password) - WallencException.Storage.NotEncrypted -> + is WallencException.Storage.NotEncrypted -> UserNotification.TextRes(R.string.error_storage_not_encrypted) - WallencException.Storage.EncInfoMissing -> + is WallencException.Storage.EncInfoMissing -> UserNotification.TextRes(R.string.error_enc_info_missing) - WallencException.Storage.DeleteRootForbidden -> + is WallencException.Storage.DeleteRootForbidden -> UserNotification.TextRes(R.string.error_delete_root_forbidden) - WallencException.Storage.NotAFile -> + is WallencException.Storage.NotAFile -> UserNotification.TextRes(R.string.error_not_a_file) - WallencException.Storage.NotADirectory -> + is WallencException.Storage.NotADirectory -> UserNotification.TextRes(R.string.error_not_a_directory) - WallencException.Storage.PathIsFile -> + is WallencException.Storage.PathIsFile -> UserNotification.TextRes(R.string.error_path_is_file) - WallencException.Storage.CannotWriteOverDirectory -> + is WallencException.Storage.CannotWriteOverDirectory -> UserNotification.TextRes(R.string.error_cannot_write_over_directory) - WallencException.Storage.UnexpectedState -> + is WallencException.Storage.UnexpectedState -> UserNotification.TextRes(R.string.error_unexpected_state) is WallencException.Storage.IoFailed -> UserNotification.TextRes(R.string.error_network) - WallencException.Auth.Failed, - WallencException.Auth.TokenMissing, + is WallencException.Auth.Failed, + is WallencException.Auth.TokenMissing, -> UserNotification.TextRes(R.string.vault_link_error_auth) - WallencException.Network.ResourceLocked -> + is WallencException.Network.ResourceLocked -> UserNotification.TextRes(R.string.error_disk_resource_locked) - WallencException.Network.OperationFailed, - WallencException.Network.OperationTimedOut, + is WallencException.Network.OperationFailed, + is WallencException.Network.OperationTimedOut, is WallencException.Network.HttpFailed, is WallencException.Network.IoFailed, -> diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskLogKeysCompose.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskLogKeysCompose.kt index a7ff4eb..b63dae5 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskLogKeysCompose.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskLogKeysCompose.kt @@ -1,22 +1,13 @@ package com.github.nullptroma.wallenc.ui.resources import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext import com.github.nullptroma.wallenc.domain.tasks.TaskLogLine @Composable fun TaskLogLine.displayText(): String { val key = logKey if (key != null) { - val context = LocalContext.current - val resolver = UiStringResolver { id, args -> - if (args.isEmpty()) { - context.getString(id) - } else { - context.getString(id, *args) - } - } - return key.resolve(resolver) + return key.resolve(composableUiStringResolver()) } return message } diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabels.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabels.kt index 1ec1603..fff0934 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabels.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabels.kt @@ -8,7 +8,8 @@ fun TaskProgressLabel.resolve(resolver: UiStringResolver): String = when (this) TaskProgressLabel.SyncNoGroups -> resolver(R.string.sync_progress_no_groups) TaskProgressLabel.SyncStarted -> resolver(R.string.sync_progress_started) TaskProgressLabel.SyncCompleted -> resolver(R.string.sync_progress_completed) - is TaskProgressLabel.SyncPreparing -> resolver(R.string.sync_progress_preparing, groupCount) + is TaskProgressLabel.SyncPreparing -> + resolver.plurals(R.plurals.sync_progress_preparing, groupCount, groupCount) is TaskProgressLabel.SyncGroupPreparing -> resolver(R.string.sync_progress_group_preparing, groupId) is TaskProgressLabel.SyncGroupNotFound -> resolver(R.string.sync_progress_group_not_found, groupId) @@ -31,7 +32,7 @@ fun TaskProgressLabel.resolve(resolver: UiStringResolver): String = when (this) is TaskProgressLabel.SyncGroupNoJournalEntries -> resolver(R.string.sync_progress_group_no_entries, groupId) is TaskProgressLabel.SyncGroupProcessingEntries -> - resolver(R.string.sync_progress_group_processing, groupId, count) + resolver.plurals(R.plurals.sync_progress_group_processing, count, groupId, count) is TaskProgressLabel.SyncGroupEntryProgress -> resolver(R.string.sync_progress_group_entry, groupId, current, total) is TaskProgressLabel.SyncGroupCompleted -> diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabelsCompose.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabelsCompose.kt index fb9a0f2..52fedf3 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabelsCompose.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabelsCompose.kt @@ -1,19 +1,7 @@ package com.github.nullptroma.wallenc.ui.resources import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext import com.github.nullptroma.wallenc.domain.tasks.TaskProgressLabel @Composable -fun TaskProgressLabel.resolveText(): String { - val context = LocalContext.current - return resolve( - UiStringResolver { id, args -> - if (args.isEmpty()) { - context.getString(id) - } else { - context.getString(id, *args) - } - }, - ) -} +fun TaskProgressLabel.resolveText(): String = resolve(composableUiStringResolver()) diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/UiStringResolver.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/UiStringResolver.kt index 1ecebe9..44b3437 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/UiStringResolver.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/UiStringResolver.kt @@ -1,8 +1,11 @@ package com.github.nullptroma.wallenc.ui.resources +import androidx.annotation.PluralsRes import androidx.annotation.StringRes /** Разрешение Android-строк для код-домена (ViewModel, без Compose). */ -fun interface UiStringResolver { +interface UiStringResolver { operator fun invoke(@StringRes id: Int, vararg formatArgs: Any): String + + fun plurals(@PluralsRes id: Int, quantity: Int, vararg formatArgs: Any): String } diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/UiStringResolverCompose.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/UiStringResolverCompose.kt new file mode 100644 index 0000000..df2cbc8 --- /dev/null +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/resources/UiStringResolverCompose.kt @@ -0,0 +1,37 @@ +package com.github.nullptroma.wallenc.ui.resources + +import androidx.annotation.StringRes +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalResources +import androidx.compose.ui.res.stringResource + +@Composable +fun composableUiStringResolver(): UiStringResolver { + val resources = LocalResources.current + return remember(resources) { + object : UiStringResolver { + override fun invoke(id: Int, vararg formatArgs: Any): String = + if (formatArgs.isEmpty()) { + resources.getString(id) + } else { + resources.getString(id, *formatArgs) + } + + override fun plurals(id: Int, quantity: Int, vararg formatArgs: Any): String = + if (formatArgs.isEmpty()) { + resources.getQuantityString(id, quantity) + } else { + resources.getQuantityString(id, quantity, *formatArgs) + } + } + } +} + +@Composable +fun resolveString(@StringRes id: Int, vararg formatArgs: Any): String = + if (formatArgs.isEmpty()) { + stringResource(id) + } else { + stringResource(id, *formatArgs) + } diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/MainViewModel.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/MainViewModel.kt index e8b9c5e..415e51a 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/MainViewModel.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/MainViewModel.kt @@ -71,12 +71,6 @@ class MainViewModel @Inject constructor( } } - fun updateRoute(qualifiedName: String, route: ScreenRoute) { - routes = routes.toMutableMap().apply { - this[qualifiedName] = route - } - } - private fun mapWorkStatus( fg: TaskForegroundUiState, pipe: PipelineState, diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/remotes/RemoteVaultsScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/remotes/RemoteVaultsScreen.kt index e3c5294..df9740c 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/remotes/RemoteVaultsScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/remotes/RemoteVaultsScreen.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.draw.alpha import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @@ -58,6 +59,7 @@ fun RemoteVaultsScreen( ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val context = LocalContext.current + val resources = LocalResources.current Box { Scaffold( @@ -215,9 +217,9 @@ fun RemoteVaultsScreen( is VaultLinkOutcome.Failed -> { val notification = outcome.reason.toUserNotification() val text = if (notification.formatArgs.isEmpty()) { - context.getString(notification.id) + resources.getString(notification.id) } else { - context.getString( + resources.getString( notification.id, *notification.formatArgs.toTypedArray(), ) diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeScreen.kt index cffc960..4ea1f39 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeScreen.kt @@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.Notes import androidx.compose.material.icons.outlined.Lock -import androidx.compose.material.icons.outlined.Notes import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeViewModel.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeViewModel.kt index 304c7e5..707da72 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeViewModel.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeViewModel.kt @@ -35,7 +35,7 @@ class StorageHomeViewModel @Inject constructor( state.value.copy( isLoading = false, storageUuid = storageUuid.toString(), - errorNotification = WallencException.Feature.StorageNotFound.toUserNotification(), + errorNotification = WallencException.Feature.StorageNotFound().toUserNotification(), ), ) return@launch @@ -59,7 +59,7 @@ class StorageHomeViewModel @Inject constructor( textSecretsCount = secrets.size, canManageDomainData = canManageDomainData, errorNotification = if (isRawEncrypted) { - WallencException.Feature.NeedsDecryptedView.toUserNotification() + WallencException.Feature.NeedsDecryptedView().toUserNotification() } else { null }, diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretDetailsViewModel.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretDetailsViewModel.kt index 15dd636..2d57fa2 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretDetailsViewModel.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretDetailsViewModel.kt @@ -48,7 +48,7 @@ class TextSecretDetailsViewModel @Inject constructor( updateState( state.value.copy( isLoading = false, - errorNotification = WallencException.Feature.StorageNotFound.toUserNotification(), + errorNotification = WallencException.Feature.StorageNotFound().toUserNotification(), ), ) return@launch @@ -58,7 +58,7 @@ class TextSecretDetailsViewModel @Inject constructor( state.value.copy( isLoading = false, isAvailable = false, - errorNotification = WallencException.Feature.NeedsDecryptedView.toUserNotification(), + errorNotification = WallencException.Feature.NeedsDecryptedView().toUserNotification(), ), ) return@launch @@ -80,7 +80,7 @@ class TextSecretDetailsViewModel @Inject constructor( isMutating = isMutating, secret = secret, errorNotification = if (secret == null) { - WallencException.Feature.SecretNotFound.toUserNotification() + WallencException.Feature.SecretNotFound().toUserNotification() } else { null }, @@ -97,7 +97,7 @@ class TextSecretDetailsViewModel @Inject constructor( if (storage.metaInfo.value.encInfo != null && !storage.isVirtualStorage) { updateState( state.value.copy( - errorNotification = WallencException.Feature.NeedsDecryptedView.toUserNotification(), + errorNotification = WallencException.Feature.NeedsDecryptedView().toUserNotification(), ), ) return@launch diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretEditViewModel.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretEditViewModel.kt index 0f9bef1..57c32ee 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretEditViewModel.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretEditViewModel.kt @@ -52,7 +52,7 @@ class TextSecretEditViewModel @Inject constructor( updateState( state.value.copy( isLoading = false, - errorNotification = WallencException.Feature.StorageNotFound.toUserNotification(), + errorNotification = WallencException.Feature.StorageNotFound().toUserNotification(), ), ) return@launch @@ -62,7 +62,7 @@ class TextSecretEditViewModel @Inject constructor( state.value.copy( isLoading = false, isAvailable = false, - errorNotification = WallencException.Feature.NeedsDecryptedView.toUserNotification(), + errorNotification = WallencException.Feature.NeedsDecryptedView().toUserNotification(), ), ) return@launch @@ -100,7 +100,7 @@ class TextSecretEditViewModel @Inject constructor( if (storage.metaInfo.value.encInfo != null && !storage.isVirtualStorage) { updateState( state.value.copy( - errorNotification = WallencException.Feature.NeedsDecryptedView.toUserNotification(), + errorNotification = WallencException.Feature.NeedsDecryptedView().toUserNotification(), ), ) return@launch diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretsViewModel.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretsViewModel.kt index bc91cd2..2b4b03e 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretsViewModel.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretsViewModel.kt @@ -33,7 +33,7 @@ class TextSecretsViewModel @Inject constructor( updateState( state.value.copy( isLoading = false, - errorNotification = WallencException.Feature.StorageNotFound.toUserNotification(), + errorNotification = WallencException.Feature.StorageNotFound().toUserNotification(), ), ) return@launch @@ -43,7 +43,7 @@ class TextSecretsViewModel @Inject constructor( state.value.copy( isLoading = false, isAvailable = false, - errorNotification = WallencException.Feature.NeedsDecryptedView.toUserNotification(), + errorNotification = WallencException.Feature.NeedsDecryptedView().toUserNotification(), ), ) return@launch diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreen.kt index 4e8ad65..4b5e195 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreen.kt @@ -28,11 +28,9 @@ import androidx.compose.material.icons.filled.ContentCopy import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.QrCodeScanner -import androidx.compose.material.icons.outlined.Lock import androidx.compose.material3.AlertDialog import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api @@ -80,7 +78,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.github.nullptroma.wallenc.domain.datatypes.TwoFaTokenRecord import com.github.nullptroma.wallenc.ui.R -import com.github.nullptroma.wallenc.ui.resources.resolveText import com.github.nullptroma.wallenc.ui.elements.QrScannerDialog import com.github.nullptroma.wallenc.usecases.TwoFaCodeState import com.github.nullptroma.wallenc.usecases.buildTwoFaCodeState @@ -256,7 +253,6 @@ fun TwoFaTokensScreen( isBusy = uiState.isMutating, onDismiss = { creating = false }, onSave = { issuer, account, secret, notes, digits, periodSeconds, algorithm -> - creating = false viewModel.saveToken( existingId = null, issuer = issuer, @@ -267,6 +263,7 @@ fun TwoFaTokensScreen( periodSeconds = periodSeconds, algorithm = algorithm, ) + creating = false }, ) } @@ -277,7 +274,6 @@ fun TwoFaTokensScreen( isBusy = uiState.isMutating, onDismiss = { editingToken = null }, onSave = { issuer, account, secret, notes, digits, periodSeconds, algorithm -> - editingToken = null viewModel.saveToken( existingId = token.id, issuer = issuer, @@ -288,6 +284,7 @@ fun TwoFaTokensScreen( periodSeconds = periodSeconds, algorithm = algorithm, ) + editingToken = null }, ) } diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensViewModel.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensViewModel.kt index 32d8009..66eca65 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensViewModel.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensViewModel.kt @@ -44,7 +44,7 @@ class TwoFaTokensViewModel @Inject constructor( updateState( state.value.copy( isLoading = false, - errorNotification = WallencException.Feature.StorageNotFound.toUserNotification(), + errorNotification = WallencException.Feature.StorageNotFound().toUserNotification(), ), ) return@launch @@ -54,7 +54,7 @@ class TwoFaTokensViewModel @Inject constructor( state.value.copy( isLoading = false, isAvailable = false, - errorNotification = WallencException.Feature.NeedsDecryptedView.toUserNotification(), + errorNotification = WallencException.Feature.NeedsDecryptedView().toUserNotification(), ), ) return@launch @@ -96,7 +96,7 @@ class TwoFaTokensViewModel @Inject constructor( if (storage.metaInfo.value.encInfo != null && !storage.isVirtualStorage) { updateState( state.value.copy( - errorNotification = WallencException.Feature.NeedsDecryptedView.toUserNotification(), + errorNotification = WallencException.Feature.NeedsDecryptedView().toUserNotification(), ), ) return@launch @@ -144,7 +144,7 @@ class TwoFaTokensViewModel @Inject constructor( if (storage.metaInfo.value.encInfo != null && !storage.isVirtualStorage) { updateState( state.value.copy( - errorNotification = WallencException.Feature.NeedsDecryptedView.toUserNotification(), + errorNotification = WallencException.Feature.NeedsDecryptedView().toUserNotification(), ), ) return@launch diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/AbstractVaultBrowserViewModel.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/AbstractVaultBrowserViewModel.kt index c058fea..9d68072 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/AbstractVaultBrowserViewModel.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/AbstractVaultBrowserViewModel.kt @@ -4,8 +4,7 @@ import androidx.annotation.StringRes import androidx.lifecycle.viewModelScope import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey import com.github.nullptroma.wallenc.domain.datatypes.Tree -import com.github.nullptroma.wallenc.domain.interfaces.IDirectory -import com.github.nullptroma.wallenc.domain.interfaces.IFile +import com.github.nullptroma.wallenc.domain.errors.toWallencException import com.github.nullptroma.wallenc.domain.interfaces.ILogger import com.github.nullptroma.wallenc.domain.interfaces.IStorage import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo @@ -14,19 +13,17 @@ import com.github.nullptroma.wallenc.domain.tasks.TaskLogLevel import com.github.nullptroma.wallenc.domain.tasks.TaskProgressLabel import com.github.nullptroma.wallenc.domain.tasks.TaskRunState import com.github.nullptroma.wallenc.domain.tasks.VaultTaskStep +import com.github.nullptroma.wallenc.ui.R +import com.github.nullptroma.wallenc.ui.ViewModelBase +import com.github.nullptroma.wallenc.ui.resources.UiStringResolver +import com.github.nullptroma.wallenc.ui.resources.UserNotification +import com.github.nullptroma.wallenc.ui.resources.toUserNotification import com.github.nullptroma.wallenc.usecases.GetOpenedStoragesUseCase import com.github.nullptroma.wallenc.usecases.ManageStoragesEncryptionUseCase import com.github.nullptroma.wallenc.usecases.ManageVaultUseCase import com.github.nullptroma.wallenc.usecases.RemoveStorageUseCase import com.github.nullptroma.wallenc.usecases.RenameStorageUseCase import com.github.nullptroma.wallenc.usecases.StorageFileManagementUseCase -import com.github.nullptroma.wallenc.ui.R -import com.github.nullptroma.wallenc.ui.ViewModelBase -import com.github.nullptroma.wallenc.ui.extensions.toPrintable -import com.github.nullptroma.wallenc.domain.errors.toWallencException -import com.github.nullptroma.wallenc.ui.resources.UiStringResolver -import com.github.nullptroma.wallenc.ui.resources.UserNotification -import com.github.nullptroma.wallenc.ui.resources.toUserNotification import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -36,7 +33,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import java.util.UUID -import kotlin.system.measureTimeMillis /** * Общая логика дерева storages для локального и удалённого vault (presentation). @@ -153,42 +149,6 @@ abstract class AbstractVaultBrowserViewModel( } } - fun printStorageInfoToLog(storage: IStorageInfo) { - val id = storage.uuid - if (isStorageTaskActive(id)) { - notifyUser(R.string.vault_msg_storage_pipeline_busy) - return - } - taskOrchestrator.enqueue( - title = uiStrings(R.string.task_title_dump_storage_log), - dispatcher = Dispatchers.IO, - busyStorageUuid = id, - work = { ctx -> - ctx.reportProgress(null, TaskProgressLabel.VaultTask(VaultTaskStep.DumpStorageLog)) - storageFileManagementUseCase.setStorage(storage) - ctx.log(TaskLogLevel.Info, uiStrings(R.string.task_log_enumerating)) - val files: List - val dirs: List - val time = measureTimeMillis { - files = storageFileManagementUseCase.getAllFiles() - dirs = storageFileManagementUseCase.getAllDirs() - } - for (file in files) { - logger.debug("Files", file.metaInfo.toString()) - } - for (dir in dirs) { - logger.debug("Dirs", dir.metaInfo.toString()) - } - logger.debug("Time", "Time: $time ms") - logger.debug("Storage", storage.toPrintable()) - ctx.log( - TaskLogLevel.Info, - uiStrings(R.string.task_log_enumerate_done, files.size, dirs.size, time), - ) - }, - ) - } - fun createStorage() { if (!state.value.addStorageFabEnabled) { logger.debug(TAG, "createStorage ignored (vault unavailable or FAB disabled)") diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/VaultBrowserScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/VaultBrowserScreen.kt index d016bc7..06ab4c5 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/VaultBrowserScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/VaultBrowserScreen.kt @@ -69,10 +69,9 @@ fun VaultBrowserScreen( null -> null } LaunchedEffect(notificationText) { - if (notificationText != null) { - Toast.makeText(context, notificationText, Toast.LENGTH_SHORT).show() - pendingNotification = null - } + val text = notificationText ?: return@LaunchedEffect + Toast.makeText(context, text, Toast.LENGTH_SHORT).show() + pendingNotification = null } val fabEnabled = uiState.addStorageFabEnabled diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/sync/StorageSyncScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/sync/StorageSyncScreen.kt index bbbbb3c..18b8932 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/sync/StorageSyncScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/sync/StorageSyncScreen.kt @@ -366,11 +366,9 @@ fun StorageSyncScreen( confirmButton = { Button( onClick = { - val groupId = pendingRemoveGroupId + val groupId = pendingRemoveGroupId ?: return@Button + viewModel.removeGroup(groupId) pendingRemoveGroupId = null - if (groupId != null) { - viewModel.removeGroup(groupId) - } }, ) { Text(text = stringResource(id = R.string.sync_confirm_delete)) @@ -399,11 +397,9 @@ fun StorageSyncScreen( confirmButton = { Button( onClick = { - val payload = pendingRemoveStorage + val payload = pendingRemoveStorage ?: return@Button + viewModel.removeStorageFromGroup(payload.first, payload.second) pendingRemoveStorage = null - if (payload != null) { - viewModel.removeStorageFromGroup(payload.first, payload.second) - } }, ) { Text(text = stringResource(id = R.string.sync_confirm_delete)) diff --git a/ui/src/main/res/values-ru/plurals.xml b/ui/src/main/res/values-ru/plurals.xml new file mode 100644 index 0000000..6d666e6 --- /dev/null +++ b/ui/src/main/res/values-ru/plurals.xml @@ -0,0 +1,15 @@ + + + + Синхронизация: подготовка %d группы + Синхронизация: подготовка %d групп + Синхронизация: подготовка %d групп + Синхронизация: подготовка %d групп + + + Синхронизация: группа «%1$s» — обработка %2$d записи + Синхронизация: группа «%1$s» — обработка %2$d записей + Синхронизация: группа «%1$s» — обработка %2$d записей + Синхронизация: группа «%1$s» — обработка %2$d записей + + diff --git a/ui/src/main/res/values-ru/strings.xml b/ui/src/main/res/values-ru/strings.xml index 4217a4d..3756e67 100644 --- a/ui/src/main/res/values-ru/strings.xml +++ b/ui/src/main/res/values-ru/strings.xml @@ -15,13 +15,10 @@ Синхронизация хранилищ Сохранение групп синхронизации Запустить синхронизацию - Запустить синхронизацию сейчас - Обновить Добавить хранилище в группу Удалить группу В группе нет хранилищ Убрать хранилище из группы - Назад Закрыть выбор хранилища Выбор хранилища для %1$s Добавить @@ -31,7 +28,6 @@ Развернуть Свернуть Создать группу синхронизации - В группе разное шифрование: задайте единый режим Несовместимые хранилища в группе: %1$d Политика шифрования группы: %1$s Не определена (группа пуста) @@ -51,8 +47,6 @@ В группы синхронизации можно добавлять только незашифрованные хранилища Для зашифрованного хранилища нужно знать пароль (откройте его перед добавлением) Хранилище не совместимо с политикой шифрования группы - Нельзя добавлять открытое виртуальное хранилище: синхронизация работает с исходными raw storage - Задача синхронизации поставлена в очередь Синхронизация уже выполняется Дождитесь окончания синхронизации Неизвестно @@ -208,7 +202,6 @@ Не зашифровано Зашифровано (открыто) Зашифровано - Текст Содержимое: %1$s Storage Статус: %1$s, %2$s @@ -217,10 +210,8 @@ зашифровано не зашифровано 2FA токены (%1$d) - Открыть 2FA Коды и секреты двухфакторной аутентификации Текстовые секреты (%1$d) - Открыть текстовые секреты Заметки, токены и произвольные пары ключ-значение Скоро здесь появятся Files, Media и другие типы данных. Добавить токен @@ -231,17 +222,13 @@ Аккаунт Секрет Заметка (опционально) - Количество цифр кода (обычно 6 или 8) - Период обновления в секундах (обычно 30) Алгоритм (SHA1, SHA256, SHA512) Количество цифр: %1$d Период обновления: %1$d с ------ - Обновление через %1$d с Обновление через %1$d с Неверный секрет или формат - Нажмите, чтобы скопировать код Сканировать QR Сканирование QR-кода TOTP QR-код не содержит валидный otpauth://totp URI @@ -259,9 +246,7 @@ Скопировать значение Сохранить Отмена - Открыть Редактировать - Неизвестно Язык Как в системе English @@ -269,7 +254,6 @@ Прошло: %1$d с / %2$d с значение Синхронизация: группы не настроены - Синхронизация: подготовка %1$d групп Синхронизация: запущена Синхронизация: завершена Синхронизация: группа «%1$s» — подготовка @@ -283,7 +267,6 @@ Синхронизация: группа «%1$s» отменена новым запуском Синхронизация: группа «%1$s» — журнал %2$d/%3$d Синхронизация: группа «%1$s» — нет записей в журнале - Синхронизация: группа «%1$s» — обработка %2$d записей Синхронизация: группа «%1$s» — запись %2$d/%3$d Синхронизация: группа «%1$s» завершена Синхронизация: группа «%1$s» — продление блокировок @@ -293,7 +276,6 @@ Синхронизация хранилищ завершена Синхронизация не удалась: %1$s Перечисление файлов и папок… - Готово: %1$d файлов, %2$d папок за %3$d мс (подробности в журнале приложения) Создание хранилища… Хранилище создано Проверка хранилища… diff --git a/ui/src/main/res/values/plurals.xml b/ui/src/main/res/values/plurals.xml new file mode 100644 index 0000000..12b9756 --- /dev/null +++ b/ui/src/main/res/values/plurals.xml @@ -0,0 +1,11 @@ + + + + Storage sync: preparing %d group + Storage sync: preparing %d groups + + + Storage sync: group "%1$s" processing %2$d entry + Storage sync: group "%1$s" processing %2$d entries + + diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 77cd255..0e68d23 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -15,13 +15,10 @@ Storage sync Saving sync groups Run sync now - Run sync now - Refresh Add storage to group Remove group No storages in this group Remove storage from group - Back Close storage picker Pick storage for %1$s Add @@ -31,7 +28,6 @@ Expand Collapse Create sync group - Mixed encryption in group: set a single mode Incompatible storages in group: %1$d Group encryption policy: %1$s Not set (group is empty) @@ -51,8 +47,6 @@ Only unencrypted storages can be added to sync groups Encrypted storage requires the password (open it before adding) Storage is not compatible with the group encryption policy - Cannot add an open virtual storage: sync works with raw storages - Sync task queued Sync is already running Wait for sync to finish Unknown @@ -208,7 +202,6 @@ Not encrypted Encrypted (open) Encrypted - Text Content: %1$s Storage Status: %1$s, %2$s @@ -217,10 +210,8 @@ encrypted not encrypted 2FA tokens (%1$d) - Open 2FA Two-factor authentication codes and secrets Text secrets (%1$d) - Open text secrets Notes, tokens, and arbitrary key-value pairs Files, Media, and more will appear here soon. Add token @@ -231,17 +222,13 @@ Account Secret Note (optional) - Code digits (usually 6 or 8) - Refresh period in seconds (usually 30) Algorithm (SHA1, SHA256, SHA512) Digits: %1$d Refresh period: %1$d s ------ - Refresh in %1$d s Refresh in %1$d s Invalid secret or format - Tap to copy code Scan QR Scan TOTP QR code QR code does not contain a valid otpauth://totp URI @@ -259,9 +246,7 @@ Copy value Save Cancel - Open Edit - Unknown Language System default English @@ -269,7 +254,6 @@ Elapsed: %1$d s / %2$d s value Storage sync: no groups configured - Storage sync: preparing %1$d groups Storage sync: started Storage sync: completed Storage sync: group "%1$s" preparing @@ -283,7 +267,6 @@ Storage sync: group "%1$s" cancelled by newer run Storage sync: group "%1$s" journal %2$d/%3$d Storage sync: group "%1$s" no journal entries - Storage sync: group "%1$s" processing %2$d entries Storage sync: group "%1$s" entry %2$d/%3$d Storage sync: group "%1$s" completed Storage sync: group "%1$s" renewing locks @@ -293,7 +276,6 @@ Storage sync finished Storage sync failed: %1$s Enumerating files and directories… - Done: %1$d files, %2$d dirs in %3$d ms (see app log for lines) Creating storage… Storage created Checking storage… diff --git a/ui/src/test/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabelsTest.kt b/ui/src/test/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabelsTest.kt index de4e4a6..a584407 100644 --- a/ui/src/test/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabelsTest.kt +++ b/ui/src/test/java/com/github/nullptroma/wallenc/ui/resources/TaskProgressLabelsTest.kt @@ -8,7 +8,11 @@ import org.junit.Test class TaskProgressLabelsTest { - private val resolver = UiStringResolver { id, _ -> "res:$id" } + private val resolver = object : UiStringResolver { + override fun invoke(id: Int, vararg formatArgs: Any): String = "res:$id" + + override fun plurals(id: Int, quantity: Int, vararg formatArgs: Any): String = "plural:$id" + } @Test fun syncNoGroups_mapsToStringRes() { diff --git a/ui/src/test/java/com/github/nullptroma/wallenc/ui/resources/WallencUserNotificationMappingTest.kt b/ui/src/test/java/com/github/nullptroma/wallenc/ui/resources/WallencUserNotificationMappingTest.kt index c0d5e10..cb229b2 100644 --- a/ui/src/test/java/com/github/nullptroma/wallenc/ui/resources/WallencUserNotificationMappingTest.kt +++ b/ui/src/test/java/com/github/nullptroma/wallenc/ui/resources/WallencUserNotificationMappingTest.kt @@ -9,13 +9,13 @@ class WallencUserNotificationMappingTest { @Test fun mapsFeatureStorageNotFound() { - val notification = WallencException.Feature.StorageNotFound.toUserNotification() + val notification = WallencException.Feature.StorageNotFound().toUserNotification() assertEquals(R.string.error_storage_not_found, notification.id) } @Test fun mapsStorageIncorrectKey() { - val notification = WallencException.Storage.IncorrectKey.toUserNotification() + val notification = WallencException.Storage.IncorrectKey().toUserNotification() assertEquals(R.string.error_incorrect_password, notification.id) } diff --git a/ui/src/test/java/com/github/nullptroma/wallenc/ui/screens/main/screens/tasks/TaskPipelineViewModelTest.kt b/ui/src/test/java/com/github/nullptroma/wallenc/ui/screens/main/screens/tasks/TaskPipelineViewModelTest.kt index a7a01d3..32920bd 100644 --- a/ui/src/test/java/com/github/nullptroma/wallenc/ui/screens/main/screens/tasks/TaskPipelineViewModelTest.kt +++ b/ui/src/test/java/com/github/nullptroma/wallenc/ui/screens/main/screens/tasks/TaskPipelineViewModelTest.kt @@ -25,6 +25,7 @@ class TaskPipelineViewModelTest { val uiStrings = mockk() every { uiStrings.invoke(any(), any()) } returns "Test task" every { uiStrings.invoke(any()) } returns "Test" + every { uiStrings.plurals(any(), any(), any()) } returns "Plural" val viewModel = TaskPipelineViewModel(orchestrator, uiStrings) viewModel.startTestTask(durationSec = 0, infinityIndeterminateProgress = false) diff --git a/usecases/build.gradle.kts b/usecases/build.gradle.kts index 777db40..7b45d56 100644 --- a/usecases/build.gradle.kts +++ b/usecases/build.gradle.kts @@ -14,7 +14,7 @@ kotlin { } dependencies { - compileOnly("javax.inject:javax.inject:1") + compileOnly(libs.javax.inject) implementation(project(":domain")) implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.serialization.json) diff --git a/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/ManageStoragesEncryptionUseCase.kt b/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/ManageStoragesEncryptionUseCase.kt index 0e48011..4b93d07 100644 --- a/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/ManageStoragesEncryptionUseCase.kt +++ b/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/ManageStoragesEncryptionUseCase.kt @@ -67,11 +67,4 @@ class ManageStoragesEncryptionUseCase @Inject constructor( unlockManager.close(storage) } - suspend fun changePassword(storage: IStorageInfo, newKey: EncryptKey, encryptPath: Boolean) { - if (storage !is IStorage) return - if (storage.metaInfo.value.encInfo == null) { - throw IllegalStateException("Storage is not encrypted") - } - storage.setEncInfo(Encryptor.generateEncryptionInfo(newKey, encryptPath)) - } } diff --git a/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/RunStorageSyncUseCase.kt b/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/RunStorageSyncUseCase.kt index e77e947..51b241a 100644 --- a/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/RunStorageSyncUseCase.kt +++ b/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/RunStorageSyncUseCase.kt @@ -34,7 +34,6 @@ class RunStorageSyncUseCase @Inject constructor( * @param logReason техническая метка для логов (не для UI) * @return false, если синхронизация уже в очереди или выполняется — новая задача не создана */ - @Suppress("UNUSED_PARAMETER") fun enqueue(displayTitle: String, logReason: String): Boolean { if (!running.compareAndSet(false, true)) { return false diff --git a/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/StorageSyncEncryptionCompat.kt b/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/StorageSyncEncryptionCompat.kt index 4539b13..e2409ce 100644 --- a/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/StorageSyncEncryptionCompat.kt +++ b/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/StorageSyncEncryptionCompat.kt @@ -4,12 +4,8 @@ import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey import com.github.nullptroma.wallenc.domain.interfaces.IStorage import com.github.nullptroma.wallenc.domain.interfaces.StorageSyncGroup import com.github.nullptroma.wallenc.domain.interfaces.StorageSyncGroupEncryptionKind -import java.util.Base64 import java.util.UUID -fun storageEncryptionSecret(key: EncryptKey): String = - Base64.getEncoder().encodeToString(key.bytes) - fun isStorageCompatibleWithGroup( storage: IStorage, group: StorageSyncGroup, diff --git a/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/StorageSyncReadiness.kt b/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/StorageSyncReadiness.kt index 215120f..8f58bb1 100644 --- a/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/StorageSyncReadiness.kt +++ b/usecases/src/main/java/com/github/nullptroma/wallenc/usecases/StorageSyncReadiness.kt @@ -3,7 +3,6 @@ package com.github.nullptroma.wallenc.usecases import com.github.nullptroma.wallenc.domain.interfaces.IStorageSyncGroupStore import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager import kotlinx.coroutines.delay -import java.util.UUID import javax.inject.Inject import javax.inject.Singleton diff --git a/vault-contracts/src/main/java/com/github/nullptroma/wallenc/vault/contract/Vaults.kt b/vault-contracts/src/main/java/com/github/nullptroma/wallenc/vault/contract/Vaults.kt index 19e1b54..fab19ca 100644 --- a/vault-contracts/src/main/java/com/github/nullptroma/wallenc/vault/contract/Vaults.kt +++ b/vault-contracts/src/main/java/com/github/nullptroma/wallenc/vault/contract/Vaults.kt @@ -16,7 +16,3 @@ val List.locals: List val List.remotes: List get() = filter { it.descriptor is VaultDescriptor.LinkedRemote } - -fun List.byBrand(brand: CloudBrand): List = filter { - (it.descriptor as? VaultDescriptor.LinkedRemote)?.brand == brand -}