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
-}