Детальный разбор перед началом работ по переносу «По душе» на Compose Multiplatform: что забэкаплено и как восстановить, что показали три пробы на макмини, какие версии совместимы, как устроить проект, что и как переносится по каждому файлу, порядок работ с контрольными точками. Продолжение разбора «По душе на iOS».
Дата: 24.09.2026. Статус: разбор выполнен, бэкап сделан, код проекта не менялся. Продолжение docs/41-ios-podushe-research-2026-09-24.md (общая картина и вопросы владельцу). Слово владельца 24.09: «можно попробовать, но сделать бэкап кода текущей версии; перед началом разработки провести более детальный ревью с изучением всех деталей более глубоко». Этот документ и есть тот ревью: каждое утверждение ниже проверено чтением кода, скриптом по всем 241 файлу или сборкой на макмини. Что проверить не удалось — сказано прямо (раздел 3.4).
backup/pre-ios-2026-09-24-main и …-release в git и на GitHub, полный бандл всех 98 ссылок репозитория на сервере и на макмини (контрольные суммы сошлись), список всех веток отдельным файлом..fig, строки с подстановками и числительными, даты по-русски, Ktor до стенда dusha (ответ 200), DataStore, Coil, QR, JPEG из Skia, инсеты, ViewModel, типизированные маршруты, схема URL. Все найденные ловушки записаны (раздел 3).app, наши AGP 8.7.3, compileSdk 35, JDK 17, Paparazzi 1.3.5 и плагин Sentry не трогаются, Compose Multiplatform 1.11.1 (май 2026), Ktor 3.2, Coil 3.4 — совместимость подтверждена проверкой метаданных и полной сборкой (проба 3). Единственная поправка к «как есть»: Paparazzi 1.3.5 не рисует Compose 1.11 (падение в layoutlib), а Paparazzi 2.0 требует AGP 8.13 и JDK 21 — значит на дороге Б всё же AGP 8.7.3 → 8.13.2 (та же 8-я линия, один модуль разрешён) и JDK 21 на макмини; с этим набором APK, снимок Paparazzi и iOS-фреймворк собрались; А — «современный стек»: AGP 9.4 + Gradle 9.7 + compileSdk 37 + JDK 21, код в отдельном модуле shared, Compose 1.12.1 — обязательная инфраструктурная работа на будущее, но её лучше делать отдельно от iOS. Рекомендация — Б сейчас, А отдельной задачей после выхода iOS.Что считается «текущей версией»: локальная main (2894cd60, снимок на начало сессии) и release/2026-09-23 (7807dc8e — из неё собрана сборка тестировщиков 0.2.0-260922.1904-dusha), плюс все остальные локальные ветки, включая те, которых нет на GitHub (4 ветки), и рабочие деревья соседних сессий.
| Что | Где | Проверка |
|---|---|---|
Метки git backup/pre-ios-2026-09-24-main → 2894cd60, backup/pre-ios-2026-09-24-release → 7807dc8e | локальный репозиторий и GitHub (git ls-remote --tags origin) | видны на origin |
Полный бандл всех ссылок plibvi-pre-ios-2026-09-24-full.bundle (458 МБ, 98 ссылок, «complete history») | /root/backups/ на сервере и ~/backups/ на макмини | git bundle verify на сервере; sha256 71ca59e2…1f31 совпала на маке |
Прирост к бандлам 18.09 и 23.09 plibvi-pre-ios-2026-09-24-inc.bundle (43 КБ) | там же | sha256 3ea81506…8de3; восстановление цепочкой 18.09 → 23.09 → 24.09 на маке: release/2026-09-23 и метки восстановились, main из прироста не выходит (её вершина — предок release, бандл такие ссылки не пишет) — поэтому основной бэкап полный бандл, прирост запасной |
Список всех веток и меток с SHA plibvi-refs-2026-09-24.txt (76 строк) | там же | — |
| Рендеры Figma (623 PNG) и прочие материалы сверки | не в git; копии на маке ~/podushe-render (623) и ~/podushe-work (5 785 PNG) | посчитано 24.09 |
Восстановление (на любой машине с git):
git clone /root/backups/plibvi-pre-ios-2026-09-24-full.bundle plibvi # или ~/backups/… на маке
cd plibvi && git checkout release/2026-09-23 # или main
Правило на время работ: перед каждым этапом Э1–Э3 (раздел 8) — свежая метка backup/<этап>-<дата> и прирост бандла на мак; Android-сборки тестировщиков до конца Э4 идут из release/2026-09-23 (или её преемницы), не из ветки переноса.
Скрипт инвентаризации (tools/-кандидат, пока в черновике сессии) прошёл по всем .kt файлам app/src (241 файл, 55 396 строк), убрав комментарии (первая версия без этого давала ложные срабатывания: слово «Activity» в KDoc, BackHandler как «Handler»). Каждый файл получил класс по худшему признаку:
| Класс | Смысл | Файлов | Строк |
|---|---|---|---|
| О | переезжает в commonMain без правок | 39 | 4 609 |
| М | механическая правка: R.* → Res.*, java.time → kotlinx-datetime, Locale, String.format, Log, BuildConfig, LocalContext | 105 | 25 922 |
| Б | замена библиотеки (Retrofit/OkHttp, Coil 2, Sentry Android, ZXing, DataStore с Context) | 11 | 1 364 |
| П | нужен второй вариант (expect/actual) — есть вызовы android.*, нативный канвас, карта, камера, файлы, разрешения, окно | 35 | 14 309 |
| Д | кадры съёмки (src/debug, DesignShots) | 33 | 7 074 |
| Т | тесты — остаются на JVM как Android unit tests | 18 | 2 118 |
Полные таблицы по файлам — приложение А. Что важно из них:
R.drawable/R.string» есть в 148 файлах — это самая массовая, но чисто механическая правка (sed + импорт Res), проверяется компиляцией.java.time» — 38 файлов, из них 9 — тесты. Основная работа — Formats.kt (359 строк, вся русская типографика дат) и Calendar.kt; остальные — типы полей OffsetDateTime/LocalDate в моделях и состояниях.TokenStore(context), PlaceSearch(context), ProfileRepository(context), rememberStrings() через LocalContext и Coil-запросы. Решается одним объектом PdPlatform (пути, чтение файлов, локация), который на Android держит Context, на iOS — ничего.android.graphics / nativeCanvas) — 17 файлов: FigDraw.kt, Buttons.kt, Frame.kt, EventMap.kt, EventCards.kt (одна строка — разбор цвета), ProfileRepository.kt (JPEG/EXIF) и 8 экранов-кадров с «арт-слоями» (велком, онбординг, обложка события, успех регистрации, город, карта, страница события, профиль) плюс SelfieNodeScreen (одна строка).String.format (6 файлов, 9 мест), System.currentTimeMillis/nanoTime (5 файлов), java.util.UUID и android.util.Base64 (3), Math.toRadians/round (2), synchronized/@Volatile (4 — @Volatile есть в kotlin.concurrent, synchronized заменяется Mutex), java.util.concurrent (TimeUnit 4, Executors 1), java.io.File/InputStream (8), runBlocking (2 — оба в сетевом слое, уйдут вместе с OkHttp).Обе пробы — отдельные проекты (~/cmp-smoke, ~/cmp-proto), не в ~/podushe-src; Kotlin 2.4.20, Compose Multiplatform 1.12.1, симулятор iPhone 17e (iOS 26.5, 1170×2532). Полные тексты проб — в черновике сессии; ниже факты.
Собралось и нарисовалось: Modifier.blur; GraphicsLayer + BlurEffect с тройной отрисовкой (модель PdGlassBackdrop); BlendMode.Plus в Compose; на Skia-канвасе — битмап ALPHA_8, MaskFilter.makeBlur, режимы PLUS и DST_OUT (модель FigDraw); PathParser из pathData ноды; BasicTextField с кириллицей; LazyColumn; два экрана NavHost. Фреймворк собирается за 1,5 мин, тулчейн Kotlin/Native (1,6 ГБ) скачался сам.
| Что | Как проверено | Итог |
|---|---|---|
| SF Pro из байтов вне композиции | Res.readBytes("files/…ttf") → androidx.compose.ui.text.platform.Font(id, bytes, weight) в iosMain; Regular 400 и Condensed 650 | ✅ узкое 650 — заголовок, обычное 400 — текст, буквы «ё», «Ъ», «₽» есть |
| Шрифт через ресурс в композиции | Font(Res.font.sf_pro_semibold, FontWeight(590)) | ✅ |
Векторные XML из .fig | painterResource(Res.drawable.ic_bell_fill) + tint, ещё две иконки | ✅ рисуются с подкраской |
| Строки с подстановками | stringResource(Res.string.fmt_ago_minutes, 5) → «5 мин назад»; %1$s → «2.4 км от вас» | ✅ |
| Числительные | pluralStringResource(…, 3, 3) → «3 общих интереса»; вне композиции getPluralString(…, 21, 21) → «21 общий интерес»; getString | ✅ русские формы one/few/many работают |
| Даты | kotlinx-datetime 0.8: LocalDate.Format { day(Padding.NONE); char(' '); monthName(RU) } → «24 сентября»; kotlin.time.Instant.parse("2026-09-24T10:00:00+03:00").toLocalDateTime(tz) | ✅ Europe/Moscow на маке |
| Сеть | Ktor 3.6 Darwin: GET https://dusha.uxbrain.ru/api/v1/public/events?limit=1 | ✅ 200, JSON |
| Хранилище | DataStore Preferences 1.2.1 через PreferenceDataStoreFactory.createWithPath в NSDocumentDirectory | ✅ пишет и читает |
| Картинки по сети | Coil 3.6.3 + coil-network-ktor3, setSingletonImageLoaderFactory с KtorNetworkFetcherFactory | ✅ логотип загрузился |
| QR | qrose 1.3.0 rememberQrCodePainter | ✅ |
| JPEG из растра | org.jetbrains.skia.Image.makeFromBitmap(bmp).encodeToData(JPEG, 90) | ✅ 1 202 байта |
| Инсеты | WindowInsets.safeDrawing: верх 47, низ 34 на iPhone 17e; WindowInsets.ime 0 без клавиатуры | ✅ |
| ViewModel, lifecycle | viewModel { CounterVm() }, LocalLifecycleOwner → RESUMED | ✅ |
| Навигация | composable<Home>, composable<Detail>, toRoute<Detail>() (типизированные маршруты, как у нас в AppNavHost) | ✅ компилируется, экран рисуется |
| «Назад» | androidx.compose.ui.backhandler.BackHandler в commonMain | ✅ компилируется (жест не проверялся) |
| Схема URL | CFBundleURLTypes + onOpenURL в Swift → Kotlin DeepLinks.open | ⚠️ симулятор показал системный вопрос «Открыть в приложении?», без клика дальше не проверить |
CADisableMinimumFrameDurationOnPhone = true Compose падает на старте с внятным текстом (проверка на 120 Гц).xcodegen + -framework) папка compose-resources/ в приложение не попадает, и любой Res.* роняет экран (MissingResourceException). Штатный путь — фаза сборки Xcode ./gradlew :app:embedAndSignAppleFrameworkForXcode, которая и линкует фреймворк, и копирует ресурсы (в пробе ресурсы скопированы руками из build/generated/compose/resourceGenerator/preparedResources/commonMain/composeResources в <app>/compose-resources/composeResources/<пакет Res>/).layer.record(size) внутри drawWithCache не компилируется — нужна форма layer.record(this, layoutDirection, size); такая строка есть в GlassBackdrop.kt (два места).nativeCanvas устарел с 1.11 — на iOS имя skiaCanvas; в 1.12 старые имена объявлены ошибкой по релиз-нотам (в пробе — предупреждение).NSFileManager.URLForDirectory) требуют @OptIn(ExperimentalForeignApi::class).shared) сборкой не проверялась — только по документации Google (раздел 4); проверится, когда придёт её очередь.imePadding, BringIntoViewRequester в Fields.kt) — только руками в симуляторе на Э3.Та же болванка получила Android-таргет: com.android.application + kotlin.multiplatform в одном модуле, флейворы local/dusha с BuildConfig, плагин Sentry 6.22 с инструментацией Logcat, Paparazzi 1.3.5 со снимком 1170×2532 через Res-ресурсы.
Первый прогон (всё новейшее: Compose Multiplatform 1.12.1, Coil 3.6.3, lifecycle 2.11.0, Ktor 3.6, AGP 8.7.3, compileSdk 35) — отказ на проверке метаданных AAR: androidx.compose.*:1.12.1 и lifecycle-*:2.11.0 «requires Android Gradle plugin 9.1.0 or higher»; coil3:*:3.6.3, qrose-android:1.3.0, okhttp-android:5.5.0 (его тянет Ktor 3.6) — «compile against version 37 or later of the Android APIs». Платформы android-37 в SDK-репозитории для наших cmdline-tools нет (android-36 поставился).
Матрица (checkDushaDebugAarMetadata, AGP 8.7.3):
| Набор | compileSdk | Итог |
|---|---|---|
| CMP 1.11.1 + Coil 3.4.0 + nav 2.9.1 + lifecycle 2.9.4 + qrose 1.0.1 + Ktor 3.6 | 35 | только okhttp-android:5.5.0 требует 37 |
| CMP 1.10.3 + Coil 3.3.0 + nav 2.9.0 + lifecycle 2.9.2 + qrose 1.0.1 + Ktor 3.6 | 35 | то же |
| CMP 1.9.3 + Coil 3.3.0 + … + Ktor 3.6 | 35 | то же |
| CMP 1.11.1 + … + Ktor 3.6 | 36 | то же (OkHttp 5.5 хочет именно 37) |
| CMP 1.11.1 + Coil 3.4.0 + nav 2.9.1 + lifecycle 2.9.4 + qrose 1.0.1 + Ktor 3.2.3 | 35 | проходит (rc=0) |
То есть сами Compose 1.9–1.11 на Android с AGP 8.7.3 и compileSdk 35 совместимы; требование AGP 9.1 появилось только у Compose 1.12 (и lifecycle 2.11).
Полная сборка набора «Б» (assembleDushaDebug, recordPaparazziDushaDebug, linkDebugFrameworkIosSimulatorArm64) — три захода:
composeApp-dusha-debug.apk (37 МБ) и iOS-фреймворк собрались, плагин Sentry отработал (задачи generateSentryBuildTimeOptions, injectSentryDebugMetaPropertiesIntoAssets), но снимок Paparazzi упал: NoSuchMethodError: android.view.View.setRequestedFrameRate(float) — layoutlib в Paparazzi 1.3.5 (январь 2025) не знает метод Android 15, который зовёт Compose 1.11 при отрисовке. То есть Paparazzi 1.3.5 несовместим с Compose 1.11 независимо от KMP — обновление Compose само по себе требует нового Paparazzi.GradleVersion.parse — конфликт библиотек Android Tools); ему нужен AGP 8.13.BUILD SUCCESSFUL за 1 мин 26 с — APK 37 МБ, снимок proto_ProtoScreenshotTest_kit.png записан (462×1000, как и сейчас режет Paparazzi), на нём русская строка из ресурсов Compose с числительным и иконка из .fig. В снимке нужен штатный приём Compose Multiplatform — обернуть содержимое в CompositionLocalProvider(LocalInspectionMode provides true) { PreviewContextConfigurationEffect(); … }, иначе ресурсы не находят Android-контекст (MissingResourceException).Тот же набор на iOS: фреймворк 1.11.1 собран, приложение запущено в симуляторе — экран пробы 2 идентичен снимку на 1.12.1, DataStore показал «запусков 2» (данные пережили переустановку между запусками).
Попутно: BackHandler для общего кода на Android живёт в отдельном артефакте org.jetbrains.compose.ui:ui-backhandler (на iOS входит в compose.ui), и с 1.11 он объявлен устаревшим в пользу NavigationEventHandler — учесть при переносе девяти вызовов.
Один модуль app остаётся Android-приложением и получает iOS-таргеты — так собрана проба 3. Структура модулей, флейворы, BuildConfig, compileSdk 35, плагин Sentry и Gradle 8.13 не меняются; меняются AGP 8.7.3 → 8.13.2 (та же линия, без запрета на один модуль), Paparazzi 1.3.5 → 2.0.0-alpha05 (иначе не рисует Compose 1.11) и JDK для Gradle 17 → 21 (требование Paparazzi 2.0; на макмини поставлен ~/jdk21, сервер сборку и так не тянет). Набор библиотек «весны–лета 2026»: Compose Multiplatform 1.11.1 (Compose 1.11 на Android, 13.05.2026), Ktor 3.2.3 (OkHttp 4.12), Coil 3.4.0, navigation 2.9.1, lifecycle 2.9.4, qrose 1.0.1 (или ZXing на Android и qrose только на iOS), kotlinx-datetime 0.8, DataStore 1.2.1. Что теряем против 1.12: iOS-стиль хендлов выделения, LayerOutsets, фиксы swipe-back и Skia m150 — некритично для паритета; догоняется на дороге А.
AGP 9.4.1 + Gradle 9.7.1 + JDK 21 + compileSdk 37 (нужны новые cmdline-tools) + Compose 1.12.1 + Coil 3.6.3 + lifecycle 2.11 + Paparazzi 2.0.0-alpha05 (поддерживает новый плагин Android-библиотеки KMP, но требует JDK 21) + Sentry 6.22 (в его журнале есть правки под AGP 9.3–9.5). Структура по требованию AGP 9: shared (общий код, плагин com.android.kotlin.multiplatform.library, блок kotlin { android { … } }, без флейворов и BuildConfig) + app (Android-приложение со встроенным Kotlin AGP 9, флейворы, Sentry, Paparazzi, MainActivity, DesignShotActivity) + iosApp. Это отдельная инфраструктурная миграция с собственной пересъёмкой; смешивать её с переносом на iOS не стоит.
Ниже — структура для дороги Б.
android-events/ — имя папки не меняется: на него завязаны tools/, macbuild, спека
app/
build.gradle.kts — плагины: com.android.application + kotlin.multiplatform +
compose + serialization + paparazzi + sentry (как в пробе 3)
src/commonMain/kotlin/ru/podushe/app/… — весь UI, состояния, репозитории, DTO (было src/main/java)
src/commonMain/composeResources/{drawable,font,values,files} — 477 картинок, 6 шрифтов, strings.xml, policy/terms.md
src/androidMain/kotlin/… — MainActivity, PodusheApp, AppContainer(Android), actual-реализации
src/androidMain/res/ — mipmap-иконки, themes.xml, xml/file_paths.xml
src/androidDebug/… — DesignShotActivity, network_security_config, pic_selfie_preview*
src/androidUnitTest/… — все нынешние тесты + Paparazzi (было src/test)
src/iosMain/kotlin/… — actual-реализации для iOS, MainViewController
iosApp/
project.yml — xcodegen: цель PoDushe, конфигурации Dusha/Dushalove/Stage/Prod,
фаза «Compile Kotlin» = ./gradlew :app:embedAndSignAppleFrameworkForXcode
PoDushe/{iOSApp.swift, Bridges/…, Info.plist, PrivacyInfo.xcprivacy, Assets.xcassets}
Package.swift или SPM в project.yml — MapLibre, Sentry Cocoa (раздел 5.4)
tools/macios, tools/shoot_design.py --backend ios — сборка и съёмка (раздел 7)
Что остаётся ровно как сейчас на Android: applicationId и суффиксы флейворов, BuildConfig-поля (в androidMain), Theme.Podushe, FileProvider, deep links podushe://, задачи :app:assembleDushaDebug, :app:lintDushaDebug, :app:testDushaDebugUnitTest, :app:recordPaparazziDushaDebug, скрипты build.sh, tools/macbuild, tools/shoot_design.py. Правило К-10 спеки не меняется.
Версии (дорога Б): Kotlin 2.4.20; Compose Multiplatform 1.11.1 (на Android — артефакты Google androidx.compose.*:1.11.x); navigation 2.9.1 и lifecycle 2.9.4 от JetBrains (на Android делегируют в androidx); AGP 8.13.2, compileSdk 35, Gradle 8.13 (ровно та версия, которую требует AGP 8.13; предупреждение Kotlin 2.4 об устаревании терпимо), JDK 21 для Gradle, Paparazzi 2.0.0-alpha05, Sentry-плагин 6.22.0.
Конфигурация стендов: на Android — BuildConfig как сейчас; на iOS — ключи Info.plist по конфигурации Xcode (PD_API_BASE_URL, PD_CHATS_BASE_URL, PD_CHAT_WS_URL, PD_ENV_NAME, PD_SENTRY_DSN), которые читает actual объект AppConfig. Один фреймворк на все схемы, стенд выбирается схемой Xcode — для TestFlight это удобнее BuildKonfig (не нужно пересобирать Kotlin под каждый стенд). GIT_SHA на iOS — тоже через Info.plist, его пишет xcodegen/скрипт сборки.
Модели без дат, чистые состояния экранов, большая часть кита (Tokens.kt, Shapes.kt, Badges.kt, Chips.kt, TabBar.kt, …), валидаторы, FigVec.kt. Список — приложение А.
| Правило | Как | Где особенно |
|---|---|---|
R.drawable.x → Res.drawable.x, painterResource(R…) → painterResource(Res…), @DrawableRes Int → DrawableResource | скрипт замены + импорты ru.podushe.app.res.*; PdImage.Res(id: Int) становится PdImage.Res(res: DrawableResource); ImageBitmap.imageResource(id) (растянутые растры с FilterQuality.None) → imageResource(Res.drawable.x) из Compose Resources | 148 файлов; Picture.kt, DesignShots |
R.string.x → Res.string.x, stringResource тот же; @StringRes Int → StringResource; R.plurals → Res.plurals | скрипт; 1 542 строки strings.xml переносятся файлом как есть (формат подстановок %1$s/%1$d тот же, проверено пробой 2) | Formats.kt, PermissionGate.kt (enum держит id строк), AppNotification-типы |
Strings/rememberStrings() (23 файла) | интерфейс остаётся; реализация без Resources: при старте приложения все строки и числительные читаются в память (Res.allStringResources + getString, миллисекунды), Strings.get остаётся синхронным; подстановки — свой formatArgs для %1$s/%1$d (69 строк с подстановками) | Formats.kt, AppViewModels.kt |
java.time → kotlinx-datetime 0.8 | OffsetDateTime → kotlin.time.Instant (+ TimeZone.currentSystemDefault() при показе); LocalDate, LocalDateTime, YearMonth → kotlinx.datetime.LocalDate, LocalDateTime, YearMonth; DateTimeFormatter.ofPattern("d MMMM", RU) → LocalDate.Format { day(Padding.NONE); char(' '); monthName(RU_GENITIVE) }; TextStyle.FULL/SHORT_STANDALONE для месяцев и дней → свои таблицы (родительный «сентября», именительный «Сентябрь», короткий «сен», дни «четверг»/«чт»); Duration.between → Instant - Instant; OffsetDateTime.parse → Instant.parse (ISO с зоной — тот же формат стенда) | Formats.kt (13 функций), Calendar.kt, Mapping.kt, Models.kt, EventsRepository.kt, состояния афиши/уведомлений/чата, DayGroups.kt; тесты FormatsTest, DayGroupsTest, ChatFeedTest, MappingTest переписываются на те же типы |
Locale("ru") | исчезает вместе с форматтерами; lowercase(RU) → lowercase() (для кириллицы без локали одинаково) | 13 файлов |
String.format(Locale.US, "%.1f", km) и "%02d".format(x) | нет в Kotlin/Native → две функции в Formats: fixed(km, 1) (умножение, округление, разделитель «.») и pad2(x); "%.5f, %.5f" координат — fixed(lat, 5) | Formats.kt (4), CreateEventViewModel, CreateFormRoute, CreateSheets, AboutForm |
System.currentTimeMillis() → Clock.System.now().toEpochMilliseconds(); System.nanoTime() (id снекбара/события) → TimeSource.Monotonic.markNow() или счётчик | Snackbar.kt, AppViewModels.kt, PhotoPicker, SelfieCameraHost, ProfileRepository | |
UUID.randomUUID() → kotlin.uuid.Uuid.random(); android.util.Base64 (JWT-клейм в TokenStore) → kotlin.io.encoding.Base64.UrlSafe | ChatSocket, Telemetry, TokenStore | |
Math.toRadians/round → kotlin.math | Geo.kt, EventsRepository.kt | |
android.util.Log → Telemetry.log (уже есть общий фасад) | 9 файлов, 45 вызовов в AppViewModels.kt | |
BuildConfig.* → AppConfig.* (expect) | 8 файлов | |
LocalContext.current для Coil → LocalPlatformContext.current (Coil 3); для resources → Strings; для файлов/разрешений — PdPlatform | 13 файлов | |
IOException → kotlinx.io.IOException (Ktor использует его же) | 8 файлов | |
@Volatile → kotlin.concurrent.Volatile; synchronized(lock) → Mutex/атомик | InterestLabels, Telemetry, EventMap (httpTuned) | |
androidx.activity.compose.BackHandler → androidx.compose.ui.backhandler.BackHandler из артефакта org.jetbrains.compose.ui:ui-backhandler (9 мест; с 1.11 помечен устаревшим в пользу NavigationEventHandler) | AppNavHost (7), CreateFormRoute, AboutStepScreen | |
TimeUnit.SECONDS → kotlin.time.Duration (уйдёт с OkHttp) | сетевой слой |
Сеть (ApiClient.kt, PodusheApi.kt, ChatsApi.kt, ServerError.kt, AuthRepository.kt, HttpJournalInterceptor.kt, PlaceSearch.kt, ChatSocket.kt): Ktor 3.2.3 (на Android движок OkHttp 4.12 — тот же, что сейчас; 3.6 тянет OkHttp 5.5 с требованием compileSdk 37). Retrofit-интерфейс на 46 ручек становится классом с 46 функциями Ktor (client.get("public/events") { parameter("limit", 50) }.body<…>()); две базы (API_BASE_URL, CHATS_BASE_URL) — два HttpClient с общим движком; Bearer — плагин defaultRequest/Auth с собственной логикой обновления: тот же мьютекс, тот же «повторить, если сосед уже обновил», тот же отдельный «голый» клиент для refresh — переносится один в один, только без runBlocking (плагин HttpSend асинхронный); retrofit2.HttpException → ResponseException (в serverDetail() и AuthRepository.reason() читается response.bodyAsText()); multipart (verifyPhoto, uploadProfilePhoto, чат-медиа — 5 мест) → MultiPartFormDataContent(formData { append("photo", bytes, Headers { ContentType, filename }) }); retrofit2.Response<Unit> → HttpResponse; таймауты connect 10 / read 20 / write 60 → плагин HttpTimeout; Photon в PlaceSearch — тот же клиент; WebSocket чата → client.webSocketSession(url) { header("Authorization", …) }, pingInterval = 25.seconds у движка (Darwin и OkHttp поддерживают), incoming → тот же SharedFlow; журнал отказов (HttpJournalInterceptor) → плагин HttpResponseValidator/ResponseObserver с чтением тела до 4 КБ; крошки Sentry для запросов — свой плагин (Sentry KMP их не даёт).
Картинки (Picture.kt, AfishaThumbnails.kt, PodusheApp.kt, EventMap.kt, экраны с AsyncImage): Coil 3.6.3: coil3.compose.AsyncImage/SubcomposeAsyncImage — те же параметры; Interceptor → coil3.intercept.Interceptor (chain.request.data, chain.size, coil3.size.Dimension.Pixels, Scale) — AfishaThumbnails переносится почти дословно; ImageLoaderFactory → SingletonImageLoader.Factory в PodusheApp (Android) и setSingletonImageLoaderFactory на iOS, движок KtorNetworkFetcherFactory (лимит 12 соединений на хост — настройка движка Ktor); allowHardware(false) — только Android; .target(onSuccess = { drawable -> … bitmap }) в маркерах карты → coil3.Image → toBitmap() (Android) / org.jetbrains.skia.Bitmap (iOS); placeholder = painterResource(id) → painterResource(Res.drawable.x); SVG — coil-svg тот же артефакт.
QR (QrCode.kt): qrose 1.3.0 даёт Painter; наши требования — цвет чернил #342D35, уровень коррекции H (логотип в центре), поле 0 — выставляются QrOptions (errorCorrectionLevel = High, padding = 0, цвет). Сетка модулей та же (стандарт QR), но перерисовку Canvas-ом по матрице, как сейчас, qrose не даёт — кадры 688:61306 и 348:47357 пересверить (в макете код нарисован контуром и всё равно «ведёт в никуда», Р-…).
Журнал (Telemetry.kt): на Android остаётся Sentry Android 8.57 с плагином (инструментация Log.*, mapping) — файл целиком уходит в androidMain как actual object Telemetry; общий expect object Telemetry { log/info/warn/error/ setUser/setScreen } — тот же API, который уже зовут 9 файлов. iOS — раздел 5.4.
DataStore (TokenStore, OnboardingFlags, RecentQueries): делегат Context.preferencesDataStore → PreferenceDataStoreFactory.createWithPath { PdPlatform.dataFile("auth.preferences_pb") } (проверено пробой 2). Токены на iOS могут жить в Keychain (multiplatform-settings 1.3) — решение при Э3: DataStore-файл в песочнице приложения iOS зашифрован системой (Data Protection), для первой версии достаточно.
Общий приём: в commonMain объявляется фасад, Android-реализация — это нынешний код, перенесённый в androidMain почти без правок; iOS-реализация — новая. Файлы-экраны при этом остаются в commonMain, из них уходят только платформенные строки.
| Узел | Файлы (строк платформенных) | Фасад в commonMain | Android (androidMain) | iOS (iosMain / Swift) |
|---|---|---|---|---|
| Нативный канвас | FigDraw.kt (весь), Buttons.kt (4 места), Frame.kt (1), 8 экранов-кадров (по 5–15 строк), EventCards.kt (1) | PdCanvasOps: saveLayer(rect, alpha), save/restore/translate/clipPath, drawPath(path, PdPaint), drawOval, drawLayer(PdBlurLayer, PdPaint); PdPaint (цвет, альфа, PdBlend PLUS/DST_IN/DST_OUT/COLOR_DODGE, линейный градиент, обводка, размытие); PdBlurLayer (битмап ALPHA_8 + origin + down); контуры остаются androidx.compose.ui.graphics.Path и конвертируются внутри реализаций (asAndroidPath() / asSkiaPath()) | как сейчас: android.graphics.Canvas/Paint/BlurMaskFilter/BitmapShader/Matrix, LruCache | org.jetbrains.skia.Canvas/Paint/Path/Bitmap(ALPHA_8)/MaskFilter.makeBlur(σ = R/2 напрямую)/Shader.makeLinearGradient/BlendMode; свой LRU на LinkedHashMap с бюджетом 24 МБ; треугольное ядро на FloatArray — общий код (Bitmap.readPixels/installPixels вместо copyPixelsToBuffer). figGlassRim, figFills, figInnerShadow, figDropShadow — через фасад |
Разбор цвета #RRGGBB | EventCards.kt:227 (android.graphics.Color.parseColor) | своя функция hexColor() — 3 строки | — | — |
| Карта | EventMap.kt (647), AppNavHost.kt (15 мест: mapRef, CameraUpdateFactory, LatLng, projection, OnCameraIdleListener), MapNodeScreen, EventPageNodeScreen, EventPageState, MapState, CreateFormRoute (8) | PdMap(events, center: PdLatLng, zoom, interactive, textureMode, onEventClick, onMapReady: (PdMapHandle) -> Unit); PdMapHandle: animateTo(lat, lng, zoom, ms), zoomBy, cameraTarget, toScreen/fromScreen, addOnIdle/removeOnIdle; PdMapSnapshot(style, center, zoom, size) для мини-карт DUSHA-71 | как сейчас (MapLibre Android 11.5.2, символьные слои, маркеры-битмапы, MapSnapshotter, TextureView/SurfaceView) | Swift-мост: в iosApp пакет MapLibre iOS 6.31 (SPM), класс MapBridge: PdMapFactory на Swift создаёт MLNMapView, стиль OpenFreeMap с тем же перекрасом слоёв, MLNShapeSource с кластеризацией (radius 64, maxZoom 13), MLNSymbolStyleLayer с iconImageName по свойству, картинки маркеров style.setImage(UIImage, forName:), тап через visibleFeatures(at:), состав кластера leaves(of:), снимки MLNMapSnapshotter. Kotlin получает UIView через UIKitView. Маркеры и стопки рисуются в общем коде на Skia (сейчас — StaticLayout/Paint на Android; на iOS TextLine/Paragraph Skia с SF Pro), результат → PNG-байты → UIImage. Плитки: свой лимит соединений — настройка URLSessionConfiguration |
| Камера селфи | SelfieCameraHost.kt (132), SelfieScreen.kt (превью) | PdSelfieCamera(onShot(path), onFailed, front/back, torch/screenFlash) как @Composable слот preview | CameraX как сейчас | AVCaptureSession + AVCaptureVideoPreviewLayer в UIKitView, AVCapturePhotoOutput → JPEG в NSTemporaryDirectory; фонарь AVCaptureDevice.torchMode; «вспышка экраном» — белый слой Compose поверх на время снимка; переворот — смена AVCaptureDeviceInput |
| Снимок в прямом положении и сжатие | ProfileRepository.kt (UprightBitmap, SelfiePng, ProfileJpeg) | PdImages.uprightPng(path, maxSide), uprightJpeg(uri, maxSide, quality) → путь файла | как сейчас (BitmapFactory, ExifInterface) | UIImage(contentsOfFile) уже учитывает EXIF-ориентацию при отрисовке в UIGraphicsImageRenderer; UIImagePNGRepresentation/UIImageJPEGRepresentation; фронтальная камера iOS не зеркалит файл — проверить на устройстве |
| Выбор фото и снимок | PhotoPicker.kt (174) | PdPhotoPicker: pickFromGallery(), takePhoto(), результат — путь/URI строкой (как сейчас String) | PickVisualMedia, TakePicture + FileProvider | PHPickerViewController (filter images, limit 1) → loadFileRepresentation → копия в кэш; снимок — UIImagePickerController(.camera) или наш PdSelfieCamera; разрешение галереи не нужно (как и на Android) |
| Разрешения | PermissionGate.kt (171) | PdPermissions.status(p), request(p): Boolean, canAskAgain(p), openSettings() | как сейчас | камера AVCaptureDevice.requestAccess(for: .video) + authorizationStatus (denied ⇒ только настройки); геолокация CLLocationManager.requestWhenInUseAuthorization + делегат; уведомления UNUserNotificationCenter.requestAuthorization; настройки UIApplication.open(openSettingsURLString). Шторки макета (732:77193, 732:76379) — общий код |
| Геолокация и геокодер | Geo.kt (123), PlaceSearch.kt (2 функции) | PdLocation.current(timeoutMs), lastKnown(); PdGeocoder.search(q, near), reverse(lat, lng) (запасной после Photon) | LocationManager, Geocoder как сейчас | CLLocationManager (делегат → suspendCancellableCoroutine, desiredAccuracy 100 м), CLGeocoder.geocodeAddressString / reverseGeocodeLocation с Locale("ru_RU"); CityCenters — общий код |
| Окно, инсеты, диалоги | Viewport.kt (findActivity, PdEdgeToEdgeDialogWindow, WindowManager), Insets.kt | PdViewport — уже общий (BoxWithConstraints); PdEdgeToEdgeDialogWindow → expect, на iOS пусто (диалоги Compose на iOS — слой в том же окне); PodusheInsets.Top → max(54.dp, safeDrawing.top) (17e: 47 → 54; Dynamic Island: 59); Bottom 24; pdNavLiftDp — WindowInsets.navigationBars на iOS = 34 → подъём 10 dp, как на Android с жестовой навигацией | как сейчас | LocalWindowInfo.containerSize там, где нужен размер окна |
| Хранилище, пути, файлы | TokenStore, OnboardingFlags, RecentQueries, PolicyScreen (openRawResource), PhotoPicker/SelfieCameraHost (cacheDir) | PdPlatform.dataFile(name), cacheFile(name), readText(Res.files); политика/условия → Res.readBytes("files/policy.md") | Context.filesDir/cacheDir | NSDocumentDirectory/NSTemporaryDirectory |
| Схема URL и «поделиться» | AppNavHost.kt (23 мест Intent: 4 «поделиться», 1 «открыть карту geo:», deep links в MainActivity), EventRegisterState | PdShare.text(text), PdExternal.openMap(lat, lng, title), DeepLinks.incoming | Intent.createChooser/ACTION_VIEW geo: | UIActivityViewController; карта — maps://?q= (Apple Maps) — решение по Figma/владельцу: на iOS открывать Apple Maps или Яндекс.Карты по URL-схеме; onOpenURL → DeepLinks (проверено до системного вопроса) |
| Телеметрия | Telemetry.kt (300), MainActivity (withSentryObservableEffect) | expect object Telemetry + TelemetryNav (крошки переходов: слушатель NavController в общем коде) | как сейчас | Swift-мост: Sentry Cocoa 8.x как SPM-пакет в iosApp, класс TelemetryBridge реализует Kotlin-интерфейс TelemetrySink (capture(level, tag, text), breadcrumb, setUser, setTag); Sentry.logger() (Sentry Logs) есть в Cocoa 8.x; dSYM на GlitchTip — sentry-cli upload-dif в скрипте архива. Альтернатива — sentry-kotlin-multiplatform 0.27 (сам линкует Cocoa через SPM), но без Logs и без интеграции Compose; мост проще и полностью под контролем |
| Точка входа и DI | MainActivity.kt, PodusheApp.kt (AppContainer(app: Application)) | AppContainer(platform: PdPlatform); AppRoot() — то, что сейчас внутри setContent {} (PdViewport { PodusheTheme { AppScaffold { AppNavHost } } }) | MainActivity зовёт AppRoot(), fixture=1/route= из intent | MainViewController(args) читает ProcessInfo.arguments (--fixture 1 --route event --id 1 --screen 158-9505 --h N --y M) |
| Шрифты | theme/Type.kt (Font(R.font) ×6) | expect val SfProFamily: FontFamily (и Condensed) — не composable, стили PdType/PodusheType остаются val | Font(R.font.sf_pro_*) как сейчас (файлы остаются и в res/font, и в composeResources/font — или только в composeResources с Font(Res.font) в композиции — решить на Э0 по итогам пересъёмки) | байты из Res.readBytes("files/sf_pro_*.ttf") при старте → androidx.compose.ui.text.platform.Font(id, bytes, weight) (проверено пробой 2: веса 400/510/590/650/700/760 попадают в свои файлы) |
| Устройство в журнале | Telemetry (Build.MANUFACTURER/MODEL/VERSION) | PdPlatform.deviceInfo() | как сейчас | UIDevice.model, systemVersion |
DesignShots.frames (623 записи guid → composable) и *Frames.kt ссылаются на R.drawable-фикстуры, DesignFixture и внутренние экраны — после замены R → Res это обычный общий код. Решение: перенести их в commonMain в пакет ru.podushe.app.design (в релизном APK их выбросит R8, в iOS-фреймворке они останутся — это допустимо на время Э3–Э4; вынести в отдельный модуль :shots можно позже, когда сверка на iOS закончится). DesignShotActivity → androidDebug; на iOS её роль выполняет аргумент запуска --screen (раздел 7). Растры pic_selfie_preview* из debug/res → composeResources (2 файла).
Переезжают в androidUnitTest без изменений смысла; тесты дат переписываются на kotlinx-datetime вместе с Formats. Paparazzi — по итогам пробы 3 (3.5). FakeStrings (тестовая реализация Strings с String.format) — на JVM может остаться, но лучше сразу на общий formatArgs.
| Ресурс | Сейчас | Куда | Особенности |
|---|---|---|---|
| Векторные иконки | res/drawable/*.xml — 108 файлов, только <vector>/<path> (проверено: ни <gradient>, ни @color, ни ?attr) | composeResources/drawable/ | поддерживаются как есть; в пробе 2 три из них нарисовались с подкраской |
| Растры | drawable-nodpi 217 файлов (104 МБ, картинки макета pic_*), drawable-hdpi/xhdpi/xxhdpi/xxxhdpi 141 файл (13 МБ) | composeResources/drawable/ (nodpi → без квалификатора, остальные — drawable-xxhdpi и т. д., квалификаторы плотности поддерживаются) | ImageBitmap.imageResource для растров с FilterQuality.None (Picture.kt) — есть в Compose Resources; размер бандла iOS вырастет на те же 117 МБ — отдельная задача сжатия после паритета |
| Шрифты | res/font/sf_pro_*.ttf 6 файлов, 1,5 МБ | composeResources/font/ и (для загрузки байтами) composeResources/files/ | см. 5.4 «Шрифты» |
| Строки | res/values/strings.xml 1 542 строки, 7 plurals, 69 с подстановками | composeResources/values/strings.xml | формат тот же; \n, кавычки, «ё» — как есть; проверка: grep спеки К-9 остаётся |
| Документы | res/raw/policy.md, terms.md | composeResources/files/ | Res.readBytes |
| Иконка приложения | mipmap-* | Android — как есть; iOS — AppIcon.appiconset 1024 из того же источника (Figma) | — |
| Тема, FileProvider, network security | values/themes.xml, xml/file_paths.xml, debug/res/xml/network_security_config.xml | остаются в androidMain/res и androidDebug/res | на iOS аналог network security — NSAppTransportSecurity только для local (http) в debug-конфигурации |
tools/macios <схема> (аналог tools/macbuild): rsync android-events/ → ~/podushe-src, xcodegen generate, xcodebuild -scheme PoDushe-Dusha -destination 'platform=iOS Simulator,id=<17e>' build; Xcode сам зовёт Gradle (embedAndSignAppleFrameworkForXcode), поэтому Kotlin собирается один раз на обе платформы. Для TestFlight — те же 01-archive-export.sh/03-upload.sh, что у «По любви», с профилем PoDushe Dusha AppStore.tools/shoot_design.py --backend ios: xcrun simctl launch <udid> com.podushe.app.dusha --screen 158-9505 [--h 1730 --y 844] → пауза → xcrun simctl io <udid> screenshot. Симулятор — iPhone 17e (iOS 26.5) или 16e (26.3): 1170×2532. Разрешения выдаются заранее xcrun simctl privacy <udid> grant camera|location … (аналог pm grant). Высокие кадры — полосами, как на Android.tools/diff_batch.py без изменений; папки docs/figma/fig/shots-ios/ и diff-ios/ рядом с андроидными, чтобы Android-эталоны не затирались.--mask-system); на iOS в маску попадает и настоящая полоса «домой» (узел Home Indicator).Ветка feature/ios-cmp от release/2026-09-23 (по правилу «своя ветка на каждую работу»); Android-сборки для тестировщиков продолжают выходить из релизной ветки. Контрольная точка («ворота») каждого шага — то, что должно быть зелёным, прежде чем шаг считается закрытым.
| Шаг | Что | Ворота |
|---|---|---|
| Э0.1 | Фасады: PdPlatform, PdCanvasOps/PdPaint/PdBlurLayer (FigDraw и 10 файлов канваса), PdMap*, PdSelfieCamera, PdPhotoPicker, PdPermissions, PdLocation/PdGeocoder, PdImages, PdShare, AppConfig, Telemetry как expect — реализации пока только Android, файлы на месте | сборка, юнит-тесты, пересъёмка кадров затронутых экранов (кит, 8 экранов с арт-слоями, карта) — diff без изменений |
| Э0.2 | Строки и ресурсы: strings.xml и картинки в composeResources, R.* → Res.*, Strings на предзагрузке, шрифты (5.4) | полная пересъёмка (шрифты и картинки касаются всех кадров) |
| Э0.3 | Даты: kotlinx-datetime в Formats, Calendar, моделях; тесты дат | юнит-тесты дат; кадры афиши, чата, уведомлений, календаря, формы события |
| Э0.4 | Сеть: Ktor вместо Retrofit/OkHttp; Coil 3; qrose; DataStore по пути | ручной проход основных сценариев на стенде dusha (вход, афиша, событие, запись, чат с фото, профиль с фото, селфи); кадры QR |
| Э0.5 | Мелочи класса М: String.format, System.*, UUID, Log, BackHandler | сборка, тесты |
| Э1 | Kotlin 2.0.21 → 2.4.20, Compose 1.7.6 → 1.11 (через Compose Multiplatform 1.11.1), navigation 2.9.1, lifecycle 2.9.4, AGP 8.7.3 → 8.13.2, Paparazzi 1.3.5 → 2.0.0-alpha05 (снимки — с PreviewContextConfigurationEffect), JDK 21 на маке; Gradle 8.13 и compileSdk 35 без изменений (дорога Б) | полная пересъёмка 623 кадров, Paparazzi, ручной проход; расхождения текста разбираются до зелёного или записываются в реестр |
| Э2 | Переезд в KMP одним коммитом в окно тишины: src/main/java → src/commonMain/kotlin, androidMain, androidDebug, androidUnitTest, iOS-таргеты, iosApp (xcodegen, фаза Gradle, Info.plist с CADisableMinimumFrameDurationOnPhone, PrivacyInfo, схема URL, тексты разрешений, иконка), tools/macios | Android: APK байт-в-байт по содержимому классов не гарантируется, поэтому — полная пересъёмка + ручной проход; iOS: приложение запускается до велкома на симуляторе |
| Э3 | iosMain + Swift-мосты: канвас на Skia, карта, камера, фото, разрешения, гео, окно, шаринг, телеметрия, шрифты, съёмка по аргументам | каждый узел — ручная проверка в симуляторе; карта и камера — на живом iPhone |
| Э4 | Сверка 623 кадров на iOS по десяткам S01…S95, разбор красных, ручной проход | REPORT-ios с теми же четырьмя числами на кадр |
| Э5 | TestFlight: App ID, запись в ASC, профили, архив, загрузка, внутренние тестеры | сборка стоит в TestFlight у команды |
Перед Э1 и Э2 — метка backup/… и прирост бандла на мак (раздел 1).
| Риск | Оценка после разбора | Чем снимаем |
|---|---|---|
| Android-сборка «поедет» при обновлении Compose 1.7.6 → 1.11 | реальный: релиз-ноты 1.8–1.11 называют убранный неявный слой у текста с режимами наложения, тихую подмену незагрузившегося шрифта, пересчёт размера полей; оптимизация BasicText при softWrap=false — уже в 1.12, на дороге Б не затрагивает | Э1 отдельно, полная пересъёмка, разбор до зелёного |
| Paparazzi | 1.3.5 не рисует Compose 1.11 (проба 3); 2.0.0-alpha05 работает, но это alpha и требует AGP 8.13 + JDK 21 | принято: Paparazzi 2.0.0-alpha05; если alpha подведёт — снимки только через эмулятор (основной конвейер сверки и так там) |
| Замороженный стек дороги Б | Compose 1.11 и Ktor 3.2 — версии 2026 года, но не последние; следующий шаг Android (Compose 1.12+) потребует AGP 9 и разделения модулей | дорога А отдельной задачей после iOS; до неё библиотеки не обновлять |
| MapLibre iOS: биндинг из Kotlin | снят Swift-мостом (Kotlin не линкует бинарный xcframework напрямую) | Э3, проверка на устройстве |
| Sentry на iOS | снят Swift-мостом; Sentry Logs есть в Cocoa 8.x | Э3 |
| Текст ±1 px на iOS | ожидаемый (Skia + CoreText против android.text + FreeType) | метрика «без текста» + inkcheck; класс остатка только по К-16 |
| Размер iOS-бандла (117 МБ растров макета + фреймворк) | известный | сжатие растров отдельной задачей после Э4; релизная сборка со stripping |
Параллельные сессии в android-events во время Э0 | средний: Э0 правит Formats, ApiClient, кит | мелкие коммиты, ежедневный rebase; объявлять соседям файлы, которые трогаются (правило 22.09) |
| Окно тишины для Э2 | нужно 1–2 дня | договориться с владельцем заранее (вопрос 2 в docs/41 §10) |
Принято здесь: дорога Б (один модуль app, версии из 4.1, включая AGP 8.13.2, Paparazzi 2.0 и JDK 21); папка android-events без переименования; конфигурация iOS через Info.plist; Swift-мосты для MapLibre и Sentry; кадры съёмки в общем коде; статус-бар max(54, системный); токены в DataStore (Keychain — позже); Paparazzi по итогам пробы 3.
За владельцем — вопросы из docs/41, раздел 10: путь общего кода (получено «можно попробовать»), окно тишины для Э2, демо-номер и удаление аккаунта на сервере, юрист по селфи, аккаунт Apple, пуши, карточка в трекере. Новый вопрос от разбора: «Открыть в картах» на iOS — Apple Maps или Яндекс.Карты по схеме URL (на Android открывается geo:-ссылка, её ловит любая карта). Предлагаю Apple Maps, иначе решает макет.
| Класс | Файлов | Строк |
|---|---|---|
| О — как есть | 39 | 4609 |
| М — механическая правка | 105 | 25922 |
| Б — замена библиотеки | 11 | 1364 |
| П — два варианта (expect/actual) | 35 | 14309 |
| Д — кадры съёмки (debug) | 33 | 7074 |
| Т — тесты (остаются на JVM) | 18 | 2118 |
| Признак | Файлов |
|---|---|
| R.drawable/R.string | 148 |
| java.time | 38 |
| Context как параметр | 24 |
| канвас android | 17 |
| Locale | 13 |
| LocalContext | 13 |
| сеть Retrofit/OkHttp | 12 |
| Coil 2 | 9 |
| Log | 9 |
| активность/окно | 6 |
| BuildConfig | 6 |
| файлы/потоки | 6 |
| String.format | 6 |
| System.* | 5 |
| Sentry Android | 4 |
| IOException | 4 |
| @Volatile/synchronized | 4 |
| TimeUnit | 4 |
| интенты/Uri | 4 |
| DataStore(Context) | 3 |
| UUID/Base64 | 3 |
| Math.* | 3 |
| потоки/Executor | 3 |
| Build.VERSION | 3 |
| карта MapLibre | 3 |
| Resources/getString | 3 |
| BackHandler(activity) | 3 |
| runBlocking | 2 |
| разрешения | 2 |
| AndroidView | 2 |
| лаунчеры | 2 |
| LruCache | 1 |
| ZXing | 1 |
| камера | 1 |
| Font(R.font) | 1 |
| Файл | Строк | Платформенные признаки | Библиотеки | Механика |
|---|---|---|---|---|
main/java/ru/podushe/app/MainActivity.kt | 96 | активность/окно | Sentry Android | BuildConfig |
main/java/ru/podushe/app/PodusheApp.kt | 108 | активность/окно | сеть Retrofit/OkHttp, Coil 2 | — |
main/java/ru/podushe/app/data/auth/TokenStore.kt | 55 | Context как параметр | DataStore(Context) | UUID/Base64 |
main/java/ru/podushe/app/data/geo/Geo.kt | 123 | разрешения, потоки/Executor, Context как параметр | — | Math.* |
main/java/ru/podushe/app/data/geo/PlaceSearch.kt | 253 | Context как параметр, Build.VERSION | сеть Retrofit/OkHttp | Locale, Log, BuildConfig, IOException, TimeUnit |
main/java/ru/podushe/app/data/prefs/OnboardingFlags.kt | 60 | Context как параметр | DataStore(Context) | — |
main/java/ru/podushe/app/data/prefs/RecentQueries.kt | 44 | Context как параметр | DataStore(Context) | — |
main/java/ru/podushe/app/data/repo/ProfileRepository.kt | 237 | канвас android, интенты/Uri, файлы/потоки, Context как параметр | сеть Retrofit/OkHttp | java.time, System.*, IOException |
main/java/ru/podushe/app/data/telemetry/Telemetry.kt | 300 | активность/окно, потоки/Executor, Context как параметр, Build.VERSION | Sentry Android | UUID/Base64, Log, BuildConfig, @Volatile/synchronized |
main/java/ru/podushe/app/ui/components/ChatComponents.kt | 262 | Context как параметр | Coil 2 | R.drawable/R.string, LocalContext |
main/java/ru/podushe/app/ui/components/EventCards.kt | 559 | канвас android, Context как параметр | Coil 2 | R.drawable/R.string, LocalContext |
main/java/ru/podushe/app/ui/components/EventMap.kt | 647 | канвас android, AndroidView, карта MapLibre, Context как параметр | сеть Retrofit/OkHttp, Coil 2 | R.drawable/R.string, LocalContext, Resources/getString, @Volatile/synchronized, TimeUnit |
main/java/ru/podushe/app/ui/components/People.kt | 240 | Context как параметр | Coil 2 | LocalContext |
main/java/ru/podushe/app/ui/format/Formats.kt | 359 | Context как параметр | — | R.drawable/R.string, java.time, Locale, String.format, LocalContext, Resources/getString |
main/java/ru/podushe/app/ui/kit/Buttons.kt | 887 | канвас android | — | R.drawable/R.string |
main/java/ru/podushe/app/ui/kit/FigDraw.kt | 361 | канвас android, файлы/потоки, LruCache | — | — |
main/java/ru/podushe/app/ui/kit/Frame.kt | 483 | канвас android | — | R.drawable/R.string |
main/java/ru/podushe/app/ui/kit/Viewport.kt | 225 | активность/окно, Context как параметр | — | LocalContext |
main/java/ru/podushe/app/ui/navigation/AppNavHost.kt | 2441 | канвас android, интенты/Uri, карта MapLibre, файлы/потоки, Context как параметр | — | R.drawable/R.string, java.time, BuildConfig, LocalContext, Resources/getString, BackHandler(activity) |
main/java/ru/podushe/app/ui/permissions/PermissionGate.kt | 171 | интенты/Uri, разрешения, лаунчеры, Context как параметр, Build.VERSION | — | R.drawable/R.string, LocalContext |
main/java/ru/podushe/app/ui/screens/afisha/EventCoverArt.kt | 172 | канвас android, Context как параметр | — | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/EventPageNodeScreen.kt | 1263 | канвас android | — | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/MapNodeScreen.kt | 892 | канвас android | — | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/auth/WelcomeNodeScreen.kt | 424 | канвас android | — | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/CreateFormRoute.kt | 424 | канвас android, карта MapLibre, Context как параметр | — | R.drawable/R.string, java.time, Locale, String.format, LocalContext, BackHandler(activity) |
main/java/ru/podushe/app/ui/screens/create/OnboardingArt.kt | 308 | канвас android, Context как параметр | — | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/legal/PolicyScreen.kt | 48 | файлы/потоки, Context как параметр | — | R.drawable/R.string, LocalContext |
main/java/ru/podushe/app/ui/screens/profile/ProfileNodeScreen.kt | 1533 | канвас android, Context как параметр | — | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/registration/CityNodeScreen.kt | 436 | канвас android | — | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/registration/PhotoPicker.kt | 174 | интенты/Uri, лаунчеры, файлы/потоки, Context как параметр | — | R.drawable/R.string, System.*, LocalContext |
main/java/ru/podushe/app/ui/screens/registration/SelfieCameraHost.kt | 132 | активность/окно, AndroidView, камера, файлы/потоки, потоки/Executor, Context как параметр | — | System.*, Log, LocalContext |
main/java/ru/podushe/app/ui/screens/registration/SelfieNodeScreen.kt | 213 | канвас android | — | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/registration/SelfieScreen.kt | 90 | Context как параметр | Coil 2 | R.drawable/R.string, LocalContext |
main/java/ru/podushe/app/ui/screens/registration/SuccessNodeScreen.kt | 183 | канвас android, Context как параметр | — | R.drawable/R.string |
main/java/ru/podushe/app/ui/theme/Type.kt | 106 | Font(R.font) | — | R.drawable/R.string |
| Файл | Строк | Библиотеки | Механика |
|---|---|---|---|
main/java/ru/podushe/app/data/api/ApiClient.kt | 156 | сеть Retrofit/OkHttp, Sentry Android | Log, BuildConfig, IOException, @Volatile/synchronized, runBlocking, TimeUnit |
main/java/ru/podushe/app/data/api/ChatsApi.kt | 68 | сеть Retrofit/OkHttp | — |
main/java/ru/podushe/app/data/api/PodusheApi.kt | 216 | сеть Retrofit/OkHttp | — |
main/java/ru/podushe/app/data/api/ServerError.kt | 33 | сеть Retrofit/OkHttp | — |
main/java/ru/podushe/app/data/auth/AuthRepository.kt | 89 | сеть Retrofit/OkHttp | — |
main/java/ru/podushe/app/data/chat/ChatSocket.kt | 93 | сеть Retrofit/OkHttp | UUID/Base64, Log, BuildConfig, runBlocking, TimeUnit |
main/java/ru/podushe/app/data/images/AfishaThumbnails.kt | 59 | Coil 2 | — |
main/java/ru/podushe/app/data/telemetry/HttpJournalInterceptor.kt | 66 | сеть Retrofit/OkHttp, Sentry Android | IOException |
main/java/ru/podushe/app/ui/kit/Picture.kt | 132 | Coil 2 | — |
main/java/ru/podushe/app/ui/kit/QrCode.kt | 56 | ZXing | — |
main/java/ru/podushe/app/ui/screens/registration/RegistrationViewModel.kt | 396 | сеть Retrofit/OkHttp | java.time, Log |
| Файл | Строк | Что |
|---|---|---|
main/java/ru/podushe/app/data/api/Mapping.kt | 225 | java.time |
main/java/ru/podushe/app/data/events/EventsRepository.kt | 639 | java.time, Locale, Math.* |
main/java/ru/podushe/app/data/mock/DesignFixtureRepository.kt | 118 | java.time |
main/java/ru/podushe/app/data/mock/MockEvents.kt | 487 | R.drawable/R.string, java.time |
main/java/ru/podushe/app/data/model/InterestCategories.kt | 49 | R.drawable/R.string |
main/java/ru/podushe/app/data/model/InterestLabels.kt | 32 | @Volatile/synchronized |
main/java/ru/podushe/app/data/model/Models.kt | 284 | java.time |
main/java/ru/podushe/app/data/validation/Validators.kt | 90 | java.time |
main/java/ru/podushe/app/ui/AppViewModels.kt | 1656 | R.drawable/R.string, java.time, System.*, Log |
main/java/ru/podushe/app/ui/ChatViewModel.kt | 174 | java.time, Log |
main/java/ru/podushe/app/ui/CreateEventViewModel.kt | 254 | R.drawable/R.string, Locale, String.format, Log |
main/java/ru/podushe/app/ui/components/AfishaBlocks.kt | 220 | R.drawable/R.string |
main/java/ru/podushe/app/ui/components/AppIcons.kt | 75 | R.drawable/R.string |
main/java/ru/podushe/app/ui/components/Calendar.kt | 229 | java.time, Locale |
main/java/ru/podushe/app/ui/components/Fields.kt | 353 | R.drawable/R.string |
main/java/ru/podushe/app/ui/components/Scaffold.kt | 441 | R.drawable/R.string |
main/java/ru/podushe/app/ui/components/Sheets.kt | 343 | R.drawable/R.string |
main/java/ru/podushe/app/ui/components/Snackbar.kt | 172 | System.* |
main/java/ru/podushe/app/ui/components/TabBar.kt | 157 | R.drawable/R.string |
main/java/ru/podushe/app/ui/debug/UiKitGallery.kt | 459 | R.drawable/R.string, java.time |
main/java/ru/podushe/app/ui/format/DayGroups.kt | 34 | java.time |
main/java/ru/podushe/app/ui/format/EventLabels.kt | 40 | R.drawable/R.string |
main/java/ru/podushe/app/ui/kit/Cards.kt | 752 | R.drawable/R.string |
main/java/ru/podushe/app/ui/kit/Fields.kt | 515 | R.drawable/R.string |
main/java/ru/podushe/app/ui/kit/Overlays.kt | 513 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/AfishaChips.kt | 38 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/AfishaPageNodeScreen.kt | 352 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/AfishaPageState.kt | 84 | R.drawable/R.string, java.time, Locale |
main/java/ru/podushe/app/ui/screens/afisha/CalendarSheetNodeScreen.kt | 193 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/CategoryPageNodeScreen.kt | 323 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/CategoryState.kt | 70 | R.drawable/R.string, java.time |
main/java/ru/podushe/app/ui/screens/afisha/ChatNodeScreen.kt | 586 | R.drawable/R.string, Math.* |
main/java/ru/podushe/app/ui/screens/afisha/CityAfishaNodeScreen.kt | 318 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/CityAfishaState.kt | 115 | R.drawable/R.string, java.time, Locale |
main/java/ru/podushe/app/ui/screens/afisha/CityListNodeScreen.kt | 293 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/CityListState.kt | 57 | R.drawable/R.string, java.time |
main/java/ru/podushe/app/ui/screens/afisha/EventMembersNodeScreen.kt | 254 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/EventMembersState.kt | 90 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/EventPageState.kt | 186 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/EventRegisterNodeScreen.kt | 515 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/EventRegisterState.kt | 99 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/FilterSheetNodeScreen.kt | 618 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/FiltersState.kt | 118 | R.drawable/R.string, java.time |
main/java/ru/podushe/app/ui/screens/afisha/MapState.kt | 83 | R.drawable/R.string, java.time |
main/java/ru/podushe/app/ui/screens/afisha/PhotoViewerNodeScreen.kt | 146 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/ReportSheetNodeScreen.kt | 243 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/ReportSheetState.kt | 46 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/SearchPageNodeScreen.kt | 255 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/SearchPageState.kt | 72 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/afisha/SheetStates.kt | 177 | R.drawable/R.string, java.time, Locale |
main/java/ru/podushe/app/ui/screens/auth/OtpNodeScreen.kt | 176 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/auth/OtpScreen.kt | 108 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/auth/PhoneNodeScreen.kt | 179 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/auth/PhoneScreen.kt | 136 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/chats/ChatFeed.kt | 75 | java.time |
main/java/ru/podushe/app/ui/screens/chats/ChatState.kt | 92 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/chats/ChatsListNodeScreen.kt | 298 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/chats/ChatsListState.kt | 59 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/chats/OtherProfileState.kt | 78 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/chats/SubscriptionsNodeScreen.kt | 131 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/common/CardMapping.kt | 32 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/CreateEventNodeScreen.kt | 1090 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/CreateEventState.kt | 412 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/CreateSheets.kt | 177 | R.drawable/R.string, java.time, Locale, String.format |
main/java/ru/podushe/app/ui/screens/create/EventQrNodeScreen.kt | 155 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/InviteNodeScreen.kt | 521 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/InviteState.kt | 106 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/ManageEventNodeScreen.kt | 252 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/ManageEventState.kt | 69 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/OnboardingArtLive.kt | 148 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/OrganizersNodeScreen.kt | 570 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/OrganizersState.kt | 141 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/ParticipantsNodeScreen.kt | 543 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/ParticipantsState.kt | 108 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/PlaceSheetNodeScreen.kt | 528 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/QuestionsNodeScreen.kt | 808 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/QuestionsState.kt | 186 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/create/UserProfileNodeScreen.kt | 648 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/favorites/FavoritesNodeScreen.kt | 332 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/favorites/FavoritesState.kt | 77 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/legal/PolicyNodeScreen.kt | 72 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/notifications/NotificationsNodeScreen.kt | 275 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/notifications/NotificationsState.kt | 92 | R.drawable/R.string, java.time, Locale |
main/java/ru/podushe/app/ui/screens/profile/DiscountsState.kt | 45 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/profile/ProfileDiscountsNodeScreen.kt | 257 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/profile/ProfileEditNodeScreen.kt | 183 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/profile/ProfileEventsNodeScreen.kt | 188 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/profile/ProfileEventsState.kt | 77 | R.drawable/R.string, java.time, Locale |
main/java/ru/podushe/app/ui/screens/profile/ProfileState.kt | 198 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/registration/AboutForm.kt | 79 | java.time, String.format |
main/java/ru/podushe/app/ui/screens/registration/AboutNodeScreen.kt | 343 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/registration/AboutStepScreen.kt | 179 | R.drawable/R.string, java.time, Locale, BackHandler(activity) |
main/java/ru/podushe/app/ui/screens/registration/CitySearchScreen.kt | 51 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/registration/DistrictSearchScreen.kt | 59 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/registration/InterestsNodeScreen.kt | 117 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/registration/LocationStepScreen.kt | 84 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/registration/SearchNodeScreen.kt | 179 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/registration/VerifyNodeScreen.kt | 145 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/registration/VerifyStepScreen.kt | 38 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/settings/BlockedNodeScreen.kt | 173 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/settings/BlockedState.kt | 61 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/settings/SettingsNodeScreen.kt | 281 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/settings/SettingsState.kt | 81 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/swipes/SwipeNodeScreen.kt | 726 | R.drawable/R.string |
main/java/ru/podushe/app/ui/screens/swipes/SwipeState.kt | 62 | R.drawable/R.string |
main/java/ru/podushe/app/data/api/Dtos.kt (480)main/java/ru/podushe/app/data/auth/AuthBranching.kt (31)main/java/ru/podushe/app/data/mock/DesignFixture.kt (246)main/java/ru/podushe/app/data/repo/FakeRepositories.kt (214)main/java/ru/podushe/app/ui/components/Buttons.kt (340)main/java/ru/podushe/app/ui/components/Cabinet.kt (214)main/java/ru/podushe/app/ui/components/Chips.kt (192)main/java/ru/podushe/app/ui/components/Controls.kt (276)main/java/ru/podushe/app/ui/components/EmptyState.kt (62)main/java/ru/podushe/app/ui/components/Pickers.kt (133)main/java/ru/podushe/app/ui/components/Rows.kt (114)main/java/ru/podushe/app/ui/kit/Badges.kt (154)main/java/ru/podushe/app/ui/kit/Click.kt (57)main/java/ru/podushe/app/ui/kit/FigVec.kt (69)main/java/ru/podushe/app/ui/kit/GlassBackdrop.kt (124)main/java/ru/podushe/app/ui/kit/LiveScroll.kt (18)main/java/ru/podushe/app/ui/kit/NodeText.kt (204)main/java/ru/podushe/app/ui/kit/Progress.kt (40)main/java/ru/podushe/app/ui/kit/Scaled.kt (21)main/java/ru/podushe/app/ui/kit/Shapes.kt (170)main/java/ru/podushe/app/ui/kit/Switch.kt (52)main/java/ru/podushe/app/ui/kit/TabBarState.kt (11)main/java/ru/podushe/app/ui/kit/Tokens.kt (125)main/java/ru/podushe/app/ui/kit/Type.kt (77)main/java/ru/podushe/app/ui/navigation/AppScaffold.kt (108)main/java/ru/podushe/app/ui/navigation/DebugRoutes.kt (74)main/java/ru/podushe/app/ui/navigation/Routes.kt (92)main/java/ru/podushe/app/ui/screens/afisha/RadiusSheetNodeScreen.kt (144)main/java/ru/podushe/app/ui/screens/auth/OtpStateMachine.kt (141)main/java/ru/podushe/app/ui/screens/chats/OtherProfileNodeScreen.kt (101)main/java/ru/podushe/app/ui/screens/create/OnboardingNodeScreen.kt (146)main/java/ru/podushe/app/ui/screens/event/CtaState.kt (82)main/java/ru/podushe/app/ui/screens/registration/InterestsStepScreen.kt (38)main/java/ru/podushe/app/ui/screens/registration/PlaceGuess.kt (39)main/java/ru/podushe/app/ui/screens/registration/VerifySuccessSheet.kt (14)main/java/ru/podushe/app/ui/theme/Color.kt (70)main/java/ru/podushe/app/ui/theme/Glass.kt (60)main/java/ru/podushe/app/ui/theme/Insets.kt (35)main/java/ru/podushe/app/ui/theme/Theme.kt (41)debug/java/ru/podushe/app/design/AfishaFrames.kt (119): R.drawable/R.stringdebug/java/ru/podushe/app/design/BlockedFrames.kt (33): R.drawable/R.stringdebug/java/ru/podushe/app/design/CalendarFrames.kt (44): R.drawable/R.stringdebug/java/ru/podushe/app/design/CategoryFrames.kt (83): R.drawable/R.stringdebug/java/ru/podushe/app/design/ChatFrames.kt (181): R.drawable/R.stringdebug/java/ru/podushe/app/design/ChatsListFrames.kt (84): R.drawable/R.stringdebug/java/ru/podushe/app/design/CityListFrames.kt (317): R.drawable/R.stringdebug/java/ru/podushe/app/design/CreateFrames.kt (466): R.drawable/R.stringdebug/java/ru/podushe/app/design/DesignShotActivity.kt (90): активность/окноdebug/java/ru/podushe/app/design/DesignShots.kt (1618): R.drawable/R.stringdebug/java/ru/podushe/app/design/EventFrames.kt (408): R.drawable/R.stringdebug/java/ru/podushe/app/design/FavoritesFrames.kt (89): R.drawable/R.stringdebug/java/ru/podushe/app/design/FilterFrames.kt (187): R.drawable/R.stringdebug/java/ru/podushe/app/design/InviteFrames.kt (117): R.drawable/R.stringdebug/java/ru/podushe/app/design/ManageFrames.kt (70): R.drawable/R.stringdebug/java/ru/podushe/app/design/MapFrames.kt (399): R.drawable/R.stringdebug/java/ru/podushe/app/design/MembersFrames.kt (95): R.drawable/R.stringdebug/java/ru/podushe/app/design/NotifyFrames.kt (114): R.drawable/R.stringdebug/java/ru/podushe/app/design/OnboardingFrames.kt (211): R.drawable/R.stringdebug/java/ru/podushe/app/design/OrganizersFrames.kt (151): R.drawable/R.stringdebug/java/ru/podushe/app/design/OtherProfileFrames.kt (261): R.drawable/R.stringdebug/java/ru/podushe/app/design/ParticipantsFrames.kt (171): R.drawable/R.stringdebug/java/ru/podushe/app/design/PlaceSheetFrames.kt (113): R.drawable/R.stringdebug/java/ru/podushe/app/design/ProfilePageFrames.kt (547): R.drawable/R.stringdebug/java/ru/podushe/app/design/QuestionsFrames.kt (330): R.drawable/R.stringdebug/java/ru/podushe/app/design/RegisterFrames.kt (119): R.drawable/R.stringdebug/java/ru/podushe/app/design/SearchFrames.kt (83): R.drawable/R.stringdebug/java/ru/podushe/app/design/SettingsFrames.kt (106): R.drawable/R.stringdebug/java/ru/podushe/app/design/SubscriptionsFrames.kt (43): R.drawable/R.stringdebug/java/ru/podushe/app/design/SupportFrames.kt (67): R.drawable/R.stringdebug/java/ru/podushe/app/design/SwipeFrames.kt (165): R.drawable/R.stringdebug/java/ru/podushe/app/design/UserChatFrames.kt (120): R.drawable/R.stringdebug/java/ru/podushe/app/design/UserProfileFrames.kt (73): R.drawable/R.stringtest/java/ru/podushe/app/data/api/MappingTest.kt (204): java.timetest/java/ru/podushe/app/data/auth/AuthBranchingTest.kt (76): —test/java/ru/podushe/app/data/geo/CityCentersTest.kt (44): —test/java/ru/podushe/app/data/images/AfishaThumbnailsTest.kt (54): —test/java/ru/podushe/app/data/validation/ValidatorsTest.kt (90): java.timetest/java/ru/podushe/app/screenshots/ScreensScreenshotTest.kt (354): Coil 2, java.timetest/java/ru/podushe/app/screenshots/UiKitScreenshotTest.kt (200): —test/java/ru/podushe/app/ui/format/DayGroupsTest.kt (56): java.timetest/java/ru/podushe/app/ui/format/FakeStrings.kt (73): R.drawable/R.string, String.formattest/java/ru/podushe/app/ui/format/FormatsTest.kt (257): java.timetest/java/ru/podushe/app/ui/format/OtpStateMachineTest.kt (135): —test/java/ru/podushe/app/ui/screens/afisha/AfishaChipsTest.kt (50): —test/java/ru/podushe/app/ui/screens/afisha/CalendarRangeTest.kt (49): java.timetest/java/ru/podushe/app/ui/screens/afisha/FiltersLogicTest.kt (86): java.timetest/java/ru/podushe/app/ui/screens/chats/ChatFeedTest.kt (110): java.timetest/java/ru/podushe/app/ui/screens/event/CtaStateTest.kt (112): —test/java/ru/podushe/app/ui/screens/registration/AboutValidatorsTest.kt (124): java.timetest/java/ru/podushe/app/ui/screens/registration/SelfieResultTest.kt (44): —