chore(openspec): archive add-android-app and sync main specs
This commit is contained in:
@@ -0,0 +1,231 @@
|
|||||||
|
# android-client Specification
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Android-клиент для self-hosted galo: настройка подключения к серверу, авторизация,
|
||||||
|
просмотр альбомов и медиа, а также фоновая загрузка фотографий и видео с телефона
|
||||||
|
с офлайн-кэшем.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### Requirement: Настройка подключения к серверу
|
||||||
|
|
||||||
|
Приложение SHALL требовать явного указания базового URL инстанса galo при первом
|
||||||
|
запуске и SHALL проверять доступность сервера перед переходом к работе.
|
||||||
|
|
||||||
|
#### Scenario: Успешная настройка
|
||||||
|
|
||||||
|
- **WHEN** пользователь вводит достижимый базовый URL и подтверждает
|
||||||
|
- **THEN** приложение проверяет `GET /api/health` и при успехе переходит к входу
|
||||||
|
|
||||||
|
#### Scenario: Сервер недоступен
|
||||||
|
|
||||||
|
- **WHEN** сервер недоступен или health вернул ошибку
|
||||||
|
- **THEN** приложение показывает ошибку и остаётся на экране настройки, сохраняя введённый URL
|
||||||
|
|
||||||
|
### Requirement: Небезопасное соединение по LAN
|
||||||
|
|
||||||
|
Приложение SHALL по умолчанию запрещать незашифрованный HTTP и SHALL разрешать его
|
||||||
|
только после явного включения пользователем режима небезопасного соединения.
|
||||||
|
|
||||||
|
#### Scenario: HTTP без разрешения
|
||||||
|
|
||||||
|
- **WHEN** пользователь указывает `http`-URL и режим небезопасного соединения выключен
|
||||||
|
- **THEN** приложение блокирует подключение и предлагает включить режим
|
||||||
|
|
||||||
|
#### Scenario: Режим включён
|
||||||
|
|
||||||
|
- **WHEN** пользователь включил режим небезопасного соединения
|
||||||
|
- **THEN** HTTP-подключение разрешено, а интерфейс явно отмечает небезопасное соединение
|
||||||
|
|
||||||
|
### Requirement: Разрешение относительных URL объектов
|
||||||
|
|
||||||
|
Приложение SHALL трактовать относительные URL объектов из REST-ответов и SSE как пути
|
||||||
|
на выбранном базовом URL сервера.
|
||||||
|
|
||||||
|
#### Scenario: Отдача объекта
|
||||||
|
|
||||||
|
- **WHEN** API возвращает `thumb_url` вида `/api/objects/<key>?...`
|
||||||
|
- **THEN** приложение запрашивает объект с того же инстанса, что и базовый URL
|
||||||
|
|
||||||
|
### Requirement: Вход и хранение сессии
|
||||||
|
|
||||||
|
Приложение SHALL позволять вход и регистрацию по email и паролю и SHALL хранить токены
|
||||||
|
только в защищённом хранилище платформы.
|
||||||
|
|
||||||
|
#### Scenario: Успешный вход
|
||||||
|
|
||||||
|
- **WHEN** вход проходит успешно
|
||||||
|
- **THEN** токены сохраняются в защищённом хранилище, пользователь видит список альбомов
|
||||||
|
|
||||||
|
#### Scenario: Перезапуск с сессией
|
||||||
|
|
||||||
|
- **WHEN** приложение перезапущено с сохранённой сессией
|
||||||
|
- **THEN** пользователь остаётся авторизованным без повторного ввода пароля
|
||||||
|
|
||||||
|
### Requirement: Автоматическое обновление токена
|
||||||
|
|
||||||
|
Приложение SHALL автоматически обновлять access-токен при ответе 401, пока refresh-токен
|
||||||
|
действителен, и SHALL завершать сессию при неудачном обновлении.
|
||||||
|
|
||||||
|
#### Scenario: Прозрачное обновление
|
||||||
|
|
||||||
|
- **WHEN** запрос получает 401 и refresh-токен действителен
|
||||||
|
- **THEN** приложение обновляет токен и повторяет исходный запрос без участия пользователя
|
||||||
|
|
||||||
|
#### Scenario: Обновление не удалось
|
||||||
|
|
||||||
|
- **WHEN** обновление токена завершилось ошибкой
|
||||||
|
- **THEN** сессия очищается и пользователь возвращается на экран входа
|
||||||
|
|
||||||
|
### Requirement: Список альбомов
|
||||||
|
|
||||||
|
Приложение SHALL показывать доступные пользователю альбомы с обложкой и счётчиками,
|
||||||
|
беря данные из API, а при отсутствии сети — из локального кэша.
|
||||||
|
|
||||||
|
#### Scenario: Онлайн
|
||||||
|
|
||||||
|
- **WHEN** авторизованный пользователь открывает приложение
|
||||||
|
- **THEN** отображается список альбомов с обложками или плейсхолдером
|
||||||
|
|
||||||
|
#### Scenario: Офлайн из кэша
|
||||||
|
|
||||||
|
- **WHEN** сеть недоступна, но кэш существует
|
||||||
|
- **THEN** список альбомов отображается из кэша с признаком офлайн-режима
|
||||||
|
|
||||||
|
### Requirement: Постраничный просмотр альбома
|
||||||
|
|
||||||
|
Приложение SHALL показывать сетку медиа альбома с постраничной загрузкой по курсору и
|
||||||
|
SHALL подгружать следующую страницу при достижении конца списка.
|
||||||
|
|
||||||
|
#### Scenario: Догрузка страницы
|
||||||
|
|
||||||
|
- **WHEN** пользователь прокручивает сетку до конца
|
||||||
|
- **THEN** приложение запрашивает следующую страницу по курсору и добавляет её к сетке
|
||||||
|
|
||||||
|
#### Scenario: Пустой альбом
|
||||||
|
|
||||||
|
- **WHEN** в альбоме нет медиа
|
||||||
|
- **THEN** отображается пустое состояние
|
||||||
|
|
||||||
|
### Requirement: Просмотр медиа на весь экран
|
||||||
|
|
||||||
|
Полноэкранный просмотр SHALL поддерживать изображение, видео с постером и управлением
|
||||||
|
воспроизведением, а также панораму 360; навигация между элементами альбома SHALL
|
||||||
|
выполняться свайпом.
|
||||||
|
|
||||||
|
#### Scenario: Свайп между элементами
|
||||||
|
|
||||||
|
- **WHEN** пользователь свайпает в просмотре
|
||||||
|
- **THEN** показывается следующий или предыдущий элемент альбома без выхода из просмотра
|
||||||
|
|
||||||
|
#### Scenario: Видео
|
||||||
|
|
||||||
|
- **WHEN** открыт видеоэлемент
|
||||||
|
- **THEN** показывается постер и доступно воспроизведение с перемоткой
|
||||||
|
|
||||||
|
### Requirement: Обновления в реальном времени
|
||||||
|
|
||||||
|
Пока экран альбома активен, приложение SHALL отображать изменения по SSE (новые,
|
||||||
|
обновлённые и удалённые медиа, социальные события) и SHALL догонять пропущенное при
|
||||||
|
переподключении.
|
||||||
|
|
||||||
|
#### Scenario: Новое фото с другого устройства
|
||||||
|
|
||||||
|
- **WHEN** другое устройство добавляет фото в открытый альбом
|
||||||
|
- **THEN** новое фото появляется в сетке без ручного обновления
|
||||||
|
|
||||||
|
#### Scenario: Переподключение
|
||||||
|
|
||||||
|
- **WHEN** SSE-соединение разорвалось и восстановилось
|
||||||
|
- **THEN** приложение дозагружает изменения, произошедшие за время разрыва
|
||||||
|
|
||||||
|
### Requirement: Выбор медиа и альбома для загрузки
|
||||||
|
|
||||||
|
Приложение SHALL предоставлять выбор одного или нескольких фото и видео из системной
|
||||||
|
галереи и SHALL требовать выбора альбома назначения перед постановкой в очередь.
|
||||||
|
|
||||||
|
#### Scenario: Постановка в очередь
|
||||||
|
|
||||||
|
- **WHEN** пользователь выбрал файлы и альбом назначения
|
||||||
|
- **THEN** задания загрузки ставятся в очередь и пользователь видит их статус
|
||||||
|
|
||||||
|
#### Scenario: Отказ сервера
|
||||||
|
|
||||||
|
- **WHEN** сервер отклоняет загрузку (например, из-за прав или размера)
|
||||||
|
- **THEN** задание помечается ошибкой с причиной от сервера
|
||||||
|
|
||||||
|
### Requirement: Фоновая очередь загрузки
|
||||||
|
|
||||||
|
Загрузка SHALL выполняться по одному файлу за HTTP-запрос и SHALL продолжаться при
|
||||||
|
уходе приложения из foreground, показывая прогресс в уведомлении; при ошибке SHALL
|
||||||
|
выполняться повтор с нарастающей задержкой.
|
||||||
|
|
||||||
|
#### Scenario: Уход в фон
|
||||||
|
|
||||||
|
- **WHEN** приложение уходит в фон во время загрузки
|
||||||
|
- **THEN** загрузка продолжается и прогресс отображается в уведомлении
|
||||||
|
|
||||||
|
#### Scenario: Повтор при ошибке
|
||||||
|
|
||||||
|
- **WHEN** загрузка файла завершилась сетевой ошибкой
|
||||||
|
- **THEN** задание повторяется, а после исчерпания попыток помечается ошибкой
|
||||||
|
|
||||||
|
### Requirement: Условия сети для загрузки
|
||||||
|
|
||||||
|
Приложение SHALL позволять ограничить загрузку сетью Wi-Fi; при ожидании подходящей
|
||||||
|
сети задание остаётся в очереди и не выполняется.
|
||||||
|
|
||||||
|
#### Scenario: Только Wi-Fi
|
||||||
|
|
||||||
|
- **WHEN** ограничение «только Wi-Fi» включено, а доступна только сотовая сеть
|
||||||
|
- **THEN** загрузка не начинается до появления Wi-Fi, а задание остаётся в очереди
|
||||||
|
|
||||||
|
### Requirement: Сохранность очереди между запусками
|
||||||
|
|
||||||
|
Очередь загрузки SHALL сохраняться на устройстве и SHALL продолжаться после перезапуска
|
||||||
|
приложения или устройства.
|
||||||
|
|
||||||
|
#### Scenario: Возобновление после перезапуска
|
||||||
|
|
||||||
|
- **WHEN** приложение или устройство перезапускается при незавершённой очереди
|
||||||
|
- **THEN** незавершённые задания возобновляются
|
||||||
|
|
||||||
|
### Requirement: Конвертация HEIC в JPEG
|
||||||
|
|
||||||
|
Файлы HEIC/HEIF SHALL конвертироваться на устройстве в JPEG перед загрузкой с
|
||||||
|
сохранением даты съёмки и геолокации.
|
||||||
|
|
||||||
|
#### Scenario: Загрузка HEIC
|
||||||
|
|
||||||
|
- **WHEN** пользователь ставит в очередь HEIC-файл
|
||||||
|
- **THEN** загружается JPEG-версия, а дата съёмки и координаты из EXIF сохраняются
|
||||||
|
|
||||||
|
#### Scenario: Ошибка конвертации
|
||||||
|
|
||||||
|
- **WHEN** конвертация завершилась ошибкой
|
||||||
|
- **THEN** задание помечается ошибкой, повреждённый файл не загружается
|
||||||
|
|
||||||
|
### Requirement: Кэш медиа по устойчивому ключу
|
||||||
|
|
||||||
|
Приложение SHALL кэшировать полученные превью и оригиналы по устойчивому идентификатору
|
||||||
|
объекта, не зависящему от подписи и срока действия URL.
|
||||||
|
|
||||||
|
#### Scenario: Смена подписи
|
||||||
|
|
||||||
|
- **WHEN** URL одного и того же объекта со временем получает новую подпись
|
||||||
|
- **THEN** приложение переиспользует закэшированный объект вместо повторной загрузки
|
||||||
|
|
||||||
|
#### Scenario: Офлайн-отдача из кэша
|
||||||
|
|
||||||
|
- **WHEN** сеть недоступна, а объект есть в кэше
|
||||||
|
- **THEN** объект отображается из кэша
|
||||||
|
|
||||||
|
### Requirement: Единый формат ошибок API
|
||||||
|
|
||||||
|
Приложение SHALL распознавать ошибки API формата `{error:{code,message}}` и SHALL
|
||||||
|
показывать пользователю понятное сообщение.
|
||||||
|
|
||||||
|
#### Scenario: Ошибка с кодом и сообщением
|
||||||
|
|
||||||
|
- **WHEN** сервер возвращает ошибку с `code` и `message`
|
||||||
|
- **THEN** приложение показывает сообщение, при необходимости адаптированное под язык интерфейса
|
||||||
Reference in New Issue
Block a user