LihonT 2 Luna beta: source release

Mod for TikTok 47.1.3 (arm64) in Material 3 style: bottom bar, profile menu, TikTok settings and
comments; downloads without the watermark; switches for ads, live streams and the Shop tab; badges of
other mods next to names and the niche mod badge (members service); app icons; mod settings screen.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
cheb0chik
2026-10-22 20:32:13 +03:00
commit ce1d870d54
121 changed files with 21570 additions and 0 deletions

126
docs/settings-model.md Normal file
View File

@@ -0,0 +1,126 @@
# Модель настроек 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
ничего не подставляет и не утверждает, что функция доступна.
- Приложение ещё не создано или файл временно не читается: для этого вызова действуют
значения по умолчанию, результат не запоминается.
- Файл повреждён или записан более новой схемой: процесс до перезапуска работает со
значениями по умолчанию; экран настроек при этом показывает ошибку и файл не трогает.