Черновик ПЗ

This commit is contained in:
2026-05-25 19:34:22 +03:00
parent adc3730b8d
commit 2b139a18b3
72 changed files with 3570 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -0,0 +1,387 @@
@startuml
scale 2
skinparam shadowing false
skinparam classFontSize 11
' PNG (лимит растра по умолчанию 4096): PLANTUML_LIMIT_SIZE=8192 java -Xmx2g -jar plantuml.jar -charset UTF-8 -tpng …
package usecases {
class ManageStoragesEncryptionUseCase {
+ enableEncryption(IStorageInfo, EncryptKey, boolean): Unit
+ openStorage(IStorageInfo, EncryptKey, boolean): Unit
+ clearAndDisableEncryption(IStorageInfo): Unit
+ closeStorage(IStorageInfo): Unit
+ canEncrypt(IStorageInfo): Unit
+ changePassword(IStorageInfo, EncryptKey, boolean): Unit
}
class RemoveStorageUseCase {
+ remove(IStorageInfo): Unit
}
class GetOpenedStoragesUseCase {
+ getOpenedStorages(): StateFlow<Map<UUID, IStorageInfo>>
}
class ManageLocalVaultUseCase {
+ getLocalStorages(): StateFlow<List<IStorageInfo>>
+ createStorage(): Unit
}
class StorageFileManagementUseCase {
+ getAllDirs(): Unit
+ setStorage(IStorageInfo): void
+ getAllFiles(): Unit
}
class RenameStorageUseCase {
+ rename(IStorageInfo, String): Unit
}
}
package tasks {
class TaskLogLine {
+ getTimestampMs(): long
+ getLevel(): TaskLogLevel
+ getMessage(): String
}
class PipelineWork {
+ run(TaskContext): Unit
}
class TaskContext {
+ reportProgress(Float, String): Unit
+ reportProgress(TaskProgress): Unit
+ log(TaskLogLevel, String): void
+ getTaskId(): TaskId
}
class PipelineState {
+ getTasks(): List
+ getRunningTaskIds(): Set
}
class ITaskOrchestrator {
+ getLogLines(): StateFlow<List<TaskLogLine>>
+ enqueue(String, PipelineWork): TaskId
+ getPipelineState(): StateFlow<PipelineState>
+ getForegroundUi(): StateFlow<TaskForegroundUiState>
+ cancel(TaskId): boolean
+ cancelAll(): void
}
class TaskProgress {
+ getLabel(): String
+ getFraction(): Float
}
class TaskForegroundUiState {
}
class TaskForegroundItem {
+ getProgress(): TaskProgress
+ getTitle(): String
+ getTaskId(): TaskId
}
class TaskLogLevel {
+ Debug
+ Warn
+ Error
+ Info
+ values(): TaskLogLevel[]
+ valueOf(String): TaskLogLevel
+ getEntries(): EnumEntries<TaskLogLevel>
}
class TaskRunState {
}
class TaskId {
+ getUuid(): UUID
}
class PipelineTask {
+ getId(): TaskId
+ getDispatcher(): CoroutineDispatcher
+ getState(): TaskRunState
+ getTitle(): String
}
}
package interfaces {
class ILogger {
+ debug(String, String): void
}
class IYandexVault {
+ getAccountEmail(): String
}
class IMetaInfo {
+ getSize(): long
+ isDeleted(): boolean
+ isHidden(): boolean
+ getLastModified(): Instant
+ getPath(): String
}
class IStorage {
+ rename(String): Unit
+ setEncInfo(StorageEncryptionInfo): Unit
+ isEmpty(): Flow<Boolean>
+ getUuid(): UUID
+ getAccessor(): IStorageAccessor
+ isAvailable(): StateFlow<Boolean>
+ getSize(): StateFlow<Long>
+ getNumberOfFiles(): StateFlow<Integer>
+ clearAllContent(): Unit
+ getMetaInfo(): StateFlow<IStorageMetaInfo>
+ isVirtualStorage(): boolean
}
class IVaultsManager {
+ getLocalVault(): IVault
+ removeRemoteVault(UUID): Unit
+ addYandexVault(String): Unit
+ getRemoteVaults(): StateFlow<List<IVault>>
+ getAllStorages(): StateFlow<List<IStorage>>
+ getAllVaults(): StateFlow<List<IVault>>
+ getUnlockManager(): IUnlockManager
}
class IStorageMetaInfo {
+ getEncInfo(): StorageEncryptionInfo
+ getName(): String
+ getLastModified(): Instant
}
class IFile {
+ getMetaInfo(): IMetaInfo
}
class IStorageInfo {
+ isEmpty(): Flow<Boolean>
+ getUuid(): UUID
+ isAvailable(): StateFlow<Boolean>
+ getSize(): StateFlow<Long>
+ getNumberOfFiles(): StateFlow<Integer>
+ getMetaInfo(): StateFlow<IStorageMetaInfo>
+ isVirtualStorage(): boolean
}
class IStorageExplorer {
+ getCurrentPath(): StateFlow<String>
}
class IVaultInfo {
+ getAvailableSpace(): StateFlow<Integer>
+ getType(): VaultType
+ getTotalSpace(): StateFlow<Integer>
+ getUuid(): UUID
+ isAvailable(): StateFlow<Boolean>
+ getStorages(): StateFlow<List<IStorageInfo>>
}
class IUnlockManager {
+ close(IStorage): Unit
+ close(UUID): Unit
+ open(IStorage, EncryptKey, boolean): Unit
+ getOpenedStorages(): StateFlow<Map<UUID, IStorage>>
}
class IDirectory {
+ getMetaInfo(): IMetaInfo
+ getElementsCount(): Integer
}
class IStorageAccessor {
+ getFilesFlow(String): Flow<DataPackage<List<IFile>>>
+ getDirs(String): Unit
+ getDirsUpdates(): SharedFlow<DataPackage<List<IDirectory>>>
+ touchDir(String): Unit
+ getFiles(String): Unit
+ getSize(): StateFlow<Long>
+ isAvailable(): StateFlow<Boolean>
+ getDirInfo(String): Unit
+ openRead(String): Unit
+ moveToTrash(String): Unit
+ delete(String): Unit
+ touchFile(String): Unit
+ getAllDirs(): Unit
+ openWrite(String): Unit
+ getAllFiles(): Unit
+ getDirsFlow(String): Flow<DataPackage<List<IDirectory>>>
+ getNumberOfFiles(): StateFlow<Integer>
+ getFilesUpdates(): SharedFlow<DataPackage<List<IFile>>>
+ setHidden(String, boolean): Unit
+ getFileInfo(String): Unit
}
class IVault {
+ getAvailableSpace(): StateFlow<Integer>
+ getType(): VaultType
+ getTotalSpace(): StateFlow<Integer>
+ remove(IStorage): Unit
+ createStorage(StorageEncryptionInfo): Unit
+ getUuid(): UUID
+ isAvailable(): StateFlow<Boolean>
+ getStorages(): StateFlow<List<IStorage>>
+ createStorage(): Unit
}
}
package encrypt {
class EncryptorWithStaticIv {
+ decryptBytesbyte[](byte[])
+ encryptStream(java.io.OutputStream): java.io.OutputStream
+ decryptStream(java.io.InputStream): java.io.InputStream
+ encryptBytesbyte[](byte[])
+ dispose(): void
+ encryptString(String): String
+ decryptString(String): String
}
class Encryptor {
+ AES_SETTINGS: String
+ IV_LEN: int
+ decryptBytesbyte[](byte[])
+ encryptStream(java.io.OutputStream): java.io.OutputStream
+ decryptStream(java.io.InputStream): java.io.InputStream
+ encryptBytesbyte[](byte[])
+ dispose(): void
+ encryptString(String): String
+ decryptString(String): String
}
}
package datatypes {
class Tree {
+ getValue(): Unit
+ getChildren(): List
+ setChildren(List): void
}
class EncryptKey {
+ toAesKey(): SecretKeySpec
+ getBytes(): byte[]
}
class DataPackage {
+ getData(): Unit
+ isError(): Boolean
+ isLoading(): Boolean
}
class DataPage {
+ getPageLength(): int
+ getPageIndex(): int
+ getHasNext(): Boolean
}
class StorageEncryptionInfo {
+ getEncryptedTestData(): String
+ getPathIv(): byte[]
}
}
package common.impl {
class CommonDirectory {
+ getElementsCount(): Integer
+ getMetaInfo(): CommonMetaInfo
}
class CommonStorageMetaInfo {
+ getEncInfo(): StorageEncryptionInfo
+ getName(): String
+ getLastModified(): Instant
}
class CommonMetaInfo {
+ getSize(): long
+ getPath(): String
+ isDeleted(): boolean
+ isHidden(): boolean
+ getLastModified(): Instant
}
class CommonFile {
+ getMetaInfo(): IMetaInfo
}
}
package auth {
class RemoteYandexAuthResult {
}
class RemoteYandexSignInLauncher {
+ launch(): void
}
}
package enums {
class VaultType {
+ DECRYPTED: VaultType
+ LOCAL: VaultType
+ YANDEX: VaultType
+ valueOf(String): VaultType
+ values(): VaultType[]
+ getEntries(): EnumEntries<VaultType>
}
}
usecases.ManageStoragesEncryptionUseCase ..> interfaces.IStorageMetaInfo
usecases.ManageStoragesEncryptionUseCase ..> interfaces.IStorageInfo
usecases.ManageStoragesEncryptionUseCase ..> tasks.TaskProgress
usecases.ManageStoragesEncryptionUseCase ..> datatypes.EncryptKey
usecases.ManageStoragesEncryptionUseCase ..> encrypt.Encryptor
usecases.ManageStoragesEncryptionUseCase ..> interfaces.IStorage
usecases.ManageStoragesEncryptionUseCase ..> interfaces.IUnlockManager
usecases.ManageStoragesEncryptionUseCase ..> datatypes.StorageEncryptionInfo
usecases.RemoveStorageUseCase ..> usecases.ManageStoragesEncryptionUseCase
usecases.RemoveStorageUseCase ..> interfaces.IStorage
usecases.RemoveStorageUseCase ..> interfaces.IStorageInfo
usecases.RemoveStorageUseCase ..> interfaces.IVaultsManager
usecases.RemoveStorageUseCase ..> interfaces.IUnlockManager
usecases.RemoveStorageUseCase ..> interfaces.IVault
tasks.TaskLogLine ..> tasks.TaskLogLevel
tasks.PipelineWork ..> tasks.TaskContext
interfaces.IVault <|.. interfaces.IYandexVault
interfaces.IVaultInfo <|.. interfaces.IYandexVault
interfaces.IYandexVault ..> interfaces.IStorage
interfaces.IYandexVault ..> interfaces.IVault
interfaces.IYandexVault ..> enums.VaultType
interfaces.IYandexVault ..> datatypes.StorageEncryptionInfo
usecases.GetOpenedStoragesUseCase ..> interfaces.IStorageInfo
usecases.GetOpenedStoragesUseCase ..> interfaces.IUnlockManager
tasks.TaskContext ..> tasks.TaskLogLevel
tasks.TaskContext ..> tasks.TaskProgress
tasks.TaskContext ..> tasks.TaskId
usecases.ManageLocalVaultUseCase ..> interfaces.IStorageInfo
usecases.ManageLocalVaultUseCase ..> interfaces.IVaultsManager
usecases.ManageLocalVaultUseCase ..> interfaces.IVault
usecases.StorageFileManagementUseCase ..> interfaces.IFile
usecases.StorageFileManagementUseCase ..> interfaces.IStorage
usecases.StorageFileManagementUseCase ..> interfaces.IStorageInfo
usecases.StorageFileManagementUseCase ..> interfaces.IDirectory
usecases.StorageFileManagementUseCase ..> interfaces.IStorageAccessor
interfaces.IStorageInfo <|.. interfaces.IStorage
interfaces.IStorage ..> interfaces.IStorageMetaInfo
interfaces.IStorage ..> interfaces.IStorageInfo
interfaces.IStorage ..> tasks.TaskProgress
interfaces.IStorage ..> interfaces.IStorageAccessor
interfaces.IStorage ..> datatypes.StorageEncryptionInfo
interfaces.IVaultsManager ..> interfaces.IStorage
interfaces.IVaultsManager ..> interfaces.IUnlockManager
interfaces.IVaultsManager ..> interfaces.IVault
interfaces.IDirectory <|.. common.impl.CommonDirectory
common.impl.CommonDirectory ..> common.impl.CommonMetaInfo
common.impl.CommonDirectory ..> interfaces.IMetaInfo
common.impl.CommonDirectory ..> interfaces.IDirectory
tasks.PipelineState ..> tasks.TaskId
tasks.PipelineState ..> tasks.PipelineTask
interfaces.IStorageMetaInfo <|.. common.impl.CommonStorageMetaInfo
common.impl.CommonStorageMetaInfo ..> interfaces.IStorageMetaInfo
common.impl.CommonStorageMetaInfo ..> datatypes.StorageEncryptionInfo
interfaces.IMetaInfo <|.. common.impl.CommonMetaInfo
common.impl.CommonMetaInfo ..> interfaces.IMetaInfo
interfaces.IStorageMetaInfo ..> datatypes.StorageEncryptionInfo
interfaces.IFile ..> interfaces.IMetaInfo
tasks.ITaskOrchestrator ..> tasks.TaskLogLine
tasks.ITaskOrchestrator ..> tasks.PipelineState
tasks.ITaskOrchestrator ..> tasks.TaskForegroundUiState
tasks.ITaskOrchestrator ..> tasks.PipelineWork
tasks.ITaskOrchestrator ..> tasks.TaskId
interfaces.IStorageInfo ..> interfaces.IStorageMetaInfo
interfaces.IStorageInfo ..> interfaces.IStorage
usecases.RenameStorageUseCase ..> interfaces.IStorage
usecases.RenameStorageUseCase ..> interfaces.IStorageInfo
interfaces.IVaultInfo ..> interfaces.IStorageInfo
interfaces.IVaultInfo ..> interfaces.IVault
interfaces.IVaultInfo ..> enums.VaultType
tasks.TaskForegroundItem ..> tasks.TaskProgress
tasks.TaskForegroundItem ..> tasks.TaskId
interfaces.IUnlockManager ..> interfaces.IStorage
interfaces.IUnlockManager ..> datatypes.EncryptKey
interfaces.IDirectory ..> interfaces.IMetaInfo
interfaces.IStorageAccessor ..> interfaces.IFile
interfaces.IStorageAccessor ..> interfaces.IDirectory
interfaces.IStorageAccessor ..> datatypes.DataPackage
interfaces.IVaultInfo <|.. interfaces.IVault
interfaces.IVault ..> interfaces.IStorage
interfaces.IVault ..> interfaces.IVaultInfo
interfaces.IVault ..> enums.VaultType
interfaces.IVault ..> datatypes.StorageEncryptionInfo
auth.RemoteYandexSignInLauncher ..> auth.RemoteYandexAuthResult
datatypes.DataPackage <|-- datatypes.DataPage
datatypes.DataPage ..> datatypes.DataPackage
interfaces.IFile <|.. common.impl.CommonFile
common.impl.CommonFile ..> interfaces.IMetaInfo
common.impl.CommonFile ..> interfaces.IFile
tasks.PipelineTask ..> tasks.TaskRunState
tasks.PipelineTask ..> tasks.TaskId
@enduml

View File

@@ -0,0 +1,80 @@
@startuml wallenc_01_start_and_sync
' Увеличенный растр для вставки в отчёт (Word / печать)
scale 3
title
Wallenc — старт приложения и параллельная синхронизация
(проектное решение; фоновая синхронизация к реализации)
end title
skinparam defaultFontName "DejaVu Sans"
skinparam activity {
BackgroundColor #F8F8F8
BorderColor #333333
DiamondBackgroundColor #E8F4FF
}
skinparam noteBackgroundColor #FFFDE7
skinparam noteBorderColor #F9A825
start
:Старт приложения (Android);
:Инициализация Room,
загрузка метаданных vault;
if (Есть сохранённые vault?) then (нет)
:Экран «первый запуск» /
создание локального vault;
else (да)
endif
if (Нужен удалённый провайдер
и нет учётной записи?) then (да)
:Экран удалённых vault /
OAuth Яндекс;
else (нет)
endif
partition "**Основной поток (UI)**" {
:(A) Главный экран:
список локальных и удалённых vault;
:Действия пользователя
(открыть, зашифровать, содержимое…);
}
fork
partition "**Фон: синхронизация (по таймеру)**" #E8F5E9 {
note right
**Проектная механика (не реализовано в коде)**
• Таймер / WorkManager Android
• Таблица в Room: UUID **storage_id**
для очереди синхронизации
• Для каждого storage — **история коммитов**
(аналог git): дерево/цепочка снимков
• Сервис: сравнение коммитов
локально vs удалённо → вычисление diff
• Применение изменений →
**одинаковое зашифрованное содержимое**
на клиенте и у провайдера
(ключи на сервер не передаются)
end note
:По срабатыванию таймера:
запуск **SyncService** / Worker;
:Чтение из БД списка
**UUID storage** из очереди;
while (Есть необработанный UUID?) is (да)
:Загрузить историю **коммитов**
для этого Storage (локально + у провайдера);
:Найти расхождения
(common ancestor / merge);
:Свести содержимое
к единому состоянию;
:Обновить очередь /
метаданные синхронизации;
endwhile (нет)
}
end fork
stop
@enduml

View File

@@ -0,0 +1,57 @@
@startuml wallenc_02_vault_lifecycle
scale 3
title
Wallenc — пользовательский поток: жизненный цикл vault
и точки постановки в очередь синхронизации (проект)
end title
skinparam defaultFontName "DejaVu Sans"
skinparam state {
BackgroundColor #F8F8F8
BorderColor #333333
}
skinparam noteBackgroundColor #E3F2FD
skinparam noteBorderColor #1565C0
state "(Б) Список vault" as List
List --> Create : Создать vault
Create --> List : Vault создан
List --> EncryptDlg : Включить шифрование
EncryptDlg --> Encrypting : Подтверждение, мастер-ключ
state Encrypting {
state "Шифрование данных + запись метаданных" as EncWork
}
Encrypting --> List : Готово
note right of Encrypting
После успешной записи **коммита**
в историю Storage (проект):
UUID storage → **очередь синхронизации**
в Room (для таймера)
end note
List --> OpenDlg : Открыть зашифрованный
OpenDlg --> Opened : Ключ верный
OpenDlg --> List : Отмена / неверный ключ
state Opened {
state "Просмотр / работа с содержимым" as Browse
}
Opened --> List : Закрыть vault / блокировка
List --> RenameDel : Переименовать / удалить
RenameDel --> List : Подтверждение
note bottom of List
**Синхронизация (проект):** любое изменение,
порождающее новый **коммит** в Storage,
добавляет storage UUID в таблицу очереди;
**SyncService** по таймеру обрабатывает очередь,
сравнивает истории коммитов с удалённой копией
и приводит зашифрованное содержимое
к одному состоянию (без передачи ключей).
end note
@enduml

View File

@@ -0,0 +1,51 @@
@startuml wallenc_03_navigation_hub
scale 3
title
Wallenc — навигация от главного экрана
и связь с фоновой синхронизацией (проект)
end title
skinparam defaultFontName "DejaVu Sans"
skinparam activityBackgroundColor #F8F8F8
skinparam activityBorderColor #333333
skinparam noteBackgroundColor #FCE4EC
skinparam noteBorderColor #C2185B
start
:(A) Главный экран:
список локальных vault;
repeat
:Ожидание действия пользователя;
backward:Назад с подэкрана;
switch (Действие?)
case (FAB / новый vault)
:Создание локального vault;
case (Выбор vault)
:Карточка / детали vault;
case (Удалённые vault)
:Экран удалённых vault;
if (Нужен OAuth Яндекс?) then (да)
:Авторизация Яндекс;
endif
case (Настройки)
:Экран настроек;
endswitch
repeat while (Пользователь в приложении?) is (да)
-> нет;
stop
floating note right
**Фон: SyncWorker (по таймеру Android) — проект**
• Room: таблица очереди с **UUID storage**
• Периодический запуск метода синхронизации
• Для каждого Storage — история **коммитов** (как git)
• Сравнение локальной и удалённой истории,
приведение зашифрованного содержимого
к одному состоянию (ключи на сервер не уходят)
• Работает **независимо** от текущего экрана UI
end note
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB