# Модель настроек LihonT 2 Первая схема настроек, 15 октября 2026. Код написан в `src/lihont/two/settings/` без зависимости от классов или хранилищ других модов. ## Значения | Поле | Тип и допустимые значения | По умолчанию | Применение после подключения | |---|---|---|---| | `region` | Строка, один из 249 ISO 3166-1 alpha-2 кодов, верхний регистр | `JP` | Следующий запуск процесса | | `profileBanners` | Boolean | `true` | Следующий запуск процесса | | `commentReposts` | Boolean | `true` | Следующий запуск процесса | | `theme` | `system`, `light`, `dark` | `system` | Следующее создание экрана настроек | Перечень стран фиксирован в модели и не зависит от локали Android. Это список стран и территорий ISO; названия для списка в интерфейсе будут локализованы отдельно. `regionIso()` возвращает код в нижнем регистре для будущих хуков. Ни телефонный префикс, ни MCC/MNC не вычисляются из позиции в этом списке. Для выбранного региона кроме `JP` метод `needsBannerWarning()` возвращает `true`: интерфейс должен предупредить, что баннеры могут быть недоступны. Для Японии предупреждения нет. Показ диалога будет реализован на этапе интерфейса. `ModSettings` неизменяемый: методы `with…` создают новый набор. По умолчанию баннеры и репосты запрошены локально, но модель не сообщает, что сервер их выдал. Тема относится к собственным экранам мода; сохранение темы пока не меняет TikTok. ## Хранилище и жизненный цикл `SettingsRepository` получает `Storage`. На первом открытии создаёт файл со значениями по умолчанию. На следующих открытиях полностью проверяет сохранённый документ. Повреждённый файл, неизвестная версия или ошибка чтения приводят к ошибке открытия и сохраняют исходные данные. Тихой замены на defaults нет. Нужен **один экземпляр репозитория на процесс приложения**. Это обязательство будущего хоста; сейчас хост и инициализация из TikTok отсутствуют. Запись из нескольких процессов не поддерживается. - `current()` — последний успешно сохранённый набор, для интерфейса. - `active()` — набор, загруженный при создании репозитория, для будущих хуков. - `restartRequired()` — различаются ли регион, баннеры или репосты между этими наборами. Возврат к активным значениям снимает признак перезапуска. Тема в это сравнение не входит. - `save()` сначала записывает полный документ, затем публикует новое состояние. Ошибка записи не меняет опубликованный набор. - `reset()` сохраняет defaults. Если активные значения отличались от defaults, необходимость перезапуска остаётся. `AndroidSettingsStorage` использует приватный `Context.getFilesDir()` и файл `lihont-settings.json`. Разрешения на файлы и доступ к сети не требуются. Запись идёт во временный файл в той же директории, выполняет `fsync`, затем атомарный `android.system.Os.rename`. Старый файл не удаляется до замены. При ошибке до замены старые данные сохраняются. Ошибки чтения не трактуются как первый запуск. Доступен на minSDK 23; шифрование настроек не требуется, секретов в схеме нет. ## Формат импорта и экспорта ```json { "format": "lihont.settings", "schemaVersion": 1, "values": { "region": "JP", "profileBanners": true, "commentReposts": true, "theme": "system" } } ``` `exportJson()` возвращает сохранённые значения. `importJson()` **заменяет весь набор**, предварительно проверив структуру, формат, версию, точные типы и допустимые значения. Частичного импорта и приведения строк к boolean нет. Лишние, отсутствующие и повторяющиеся ключи, неизвестные страны и темы, повреждённый JSON, `null`, массивы, дробная версия, данные после документа и документ длиннее 16 384 символов отклоняются. Файл Android читается как строгий UTF-8 и ограничен 16 384 байтами; стандартный экспорт содержит только ASCII. Ошибка проверки не вызывает запись. Ошибка записи не обновляет `current()`. Это **первая** схема: предыдущих форматов настроек у LihonT 2 нет. Поэтому версии 0 и выше 1 отклоняются с сохранением файла. При появлении схемы 2 потребуется явная миграция 1 → 2 и её тесты до изменения номера. Настройки старой серии и других модов автоматически не импортируются. Формат и API готовы для будущего интерфейса резервных копий. Кнопки экспорта и импорта в APK на этом этапе не добавляются. ## Граница текущего этапа Сейчас это модель, кодек и адаптер хранения. Они ещё не подключены к экрану TikTok Settings, региональным вызовам, флагам баннеров или репостам. Существующий `LihonTRegion` в прежнем тестовом APK по-прежнему возвращает фиксированный `by`. Новые значения `JP` не выдаются за изменение аккаунта или серверного региона. Следующий этап — собственный хост настроек с инициализацией репозитория и обработкой ошибок хранения. Затем подключаются функции по roadmap и проверяются на устройстве. ## Проверка Из корня проекта: ```powershell py -m unittest discover -s tests -v py -c "from lihont.toolchain import Toolchain; print(Toolchain('tools').compile_dex('src/lihont/two', 'work/settings-model/runtime', 23))" ``` JVM-тесты компилируют и исполняют реальные `ModSettings`, `SettingsCodec` и `SettingsRepository`. Android-адаптер компилируется против `android.jar`, затем весь runtime проходит D8 с minAPI 23. JVM-тесты не подменяют проверку Android файловой системы на устройстве; её включаем в интеграционный этап. ## Подключение флагов TikTok `lihont.two.settings.SettingsHost` держит единственный `SettingsRepository` процесса. Им пользуются экран настроек и `lihont.two.LihonTFlags`, поэтому `active()` — это то, что видели хуки с первого чтения, а `restartRequired()` сравнивает экран именно с ним. - Переключатель включён: чтение флага возвращает значение LihonT (см. `profile_features.py`). - Переключатель выключен: возвращается значение, которое прочитал сам TikTok. LihonT ничего не подставляет и не утверждает, что функция доступна. - Приложение ещё не создано или файл временно не читается: для этого вызова действуют значения по умолчанию, результат не запоминается. - Файл повреждён или записан более новой схемой: процесс до перезапуска работает со значениями по умолчанию; экран настроек при этом показывает ошибку и файл не трогает.