---
name: cebersprint-overview
description: "CebersPrint — Android-приложение для печати на mac mini, цель — клон NokoPrint без зависимости от хоста. v0.21.0 (phase14), 192 unit-теста, главное недоделанное — CAPT-2 encoder."
metadata: 
  node_type: memory
  type: project
  originSessionId: 0ef3c3d5-9107-4c6a-9ad6-9a8f9855a024
---

**Проект:** CebersPrint — Android Kotlin/Compose app, минимально SDK 23 / target 34. namespace `com.cebers.print`.

**Локация:** `cebers_mini@192.168.0.107:~/Разработки/CebersPrint/phase14` — развёрнут git-репозитория из `CebersPrint_phase14_snapshot.zip` (2026-05-20, ветка `main`, baseline-коммит `e4ec1d6`). Снапшоты лежат в `~/Downloads/Projects/CebersPrint/` (phase6..phase14, zip+apk).

**Версия:** 0.21.0, versionCode 21, 192 JVM unit-теста (последний раз зелёные на момент phase14 build).

**Стек:** Compose, Room (ksp), WorkManager, USB host, Wi-Fi/Bluetooth/BLE/Email transports, IPP server. 133 main.kt + 19 test.kt + 0 androidTest. Gradle 8.7, JDK 17.

**Архитектура транспорта (важно):**
- `data/PrintDispatcher.kt` — format negotiation + transcoding (PDF→PWG/URF/PCL5)
- `domain/transport/PrintTransport.kt` — общий интерфейс
- `data/usb/UsbPrintTransport.kt` — USB-ветка, читает IEEE 1284 device ID, классифицирует host-based через `UsbHostBasedDetector`
- `data/usb/capt/` — Canon CAPT-1 (SCoA-1) encoder + transport (LBP-2900/3000/3010/3050, **только PDF**)
- `data/wifi/` — IPP/LPD/BJNP/RAW socket
- `data/bluetooth/` — Phomemo/Niimbot BLE encoders
- `data/email/` — SMTP transport
- `render/` — PdfPageRasterizer, PwgRasterEncoder, UrfEncoder, Pcl5Wrapper, RasterPipeline
- `util/` — 40+ document renderers (DOCX, ODT, EPUB, FB2, HTML, MD, PPTX, XLSX, RTF, CSV, EML), barcode, watermark, booklet, N-up

**Главная задача (sprint 15):** clean-room reverse CAPT-2 / UFR-II протокола для **Canon LBP6000** (VID 0x04A9, PID 0x26E6, `Family.Capt2` в `CaptDevices.kt`). NokoPrint на этом принтере печатает с Android по USB без проблем — наша цель повторить функционал чистым реверсом (без декомпиляции NokoPrint).

**Лицензионная ловушка:** captdriver (Galakhov) — GPL-2, его портирование заразит CebersPrint вирусной лицензией. Юзер хочет «для себя/родственников, потом продавать» — это уже distribution в смысле GPL, значит **captdriver брать нельзя**, идём через USB-snoop NokoPrint.

**Sprint 15 этапы (см. также [[cebersprint-sprint15-plan]]):**
- 15.A: git + аудит + image→PDF для CAPT-ветки + честные сообщения CAPT-2 + snoop-инфра (без принтера, 2-3 дня)
- 15.B: USB-snoop NokoPrint↔LBP6000, реверс протокола (нужен принтер + Android, 1-2 нед)
- 15.C: Capt2Encoder + Capt2Transport + интеграция (без принтера для черновика, 1-2 нед)
- 15.D: отладка на железе LBP6000 (нужен принтер, 3-7 дн)
- 15.E: deferred-фичи (AirPrint subtype, cloud backup), финальный аудит, релиз

**Известные ограничения (из STATUS_PHASE14.md):**
- Cloud backup (Drive/WebDAV) — deferred
- AirPrint `_universal._sub._ipp._tcp` — deferred
- 14.A periodic worker — минимум 24h period (ограничение WorkManager)
- 14.D reprint — без clone copies/watermark/page-range options
- CAPT-2 распознаётся, но не печатает (отсюда главная задача sprint 15)

**Toolchain статус (2026-05-20):** на mac mini **нет JDK и Android SDK** — снапшоты собирались где-то ещё. Перед sprint надо поставить openjdk@17 + Android cmdline-tools + platforms/build-tools.

**Workflow до этого:** до 2026-05-20 работа велась только через zip-снапшоты (phase6..14), без git. Развернули git-репо 2026-05-20.

**Прогресс sprint 15 (2026-05-20, версия 0.22.0):**
- ✅ 15.A.1 [`649a743`] — авто-конвертация image/docx/odt/etc → PDF в CAPT-ветке через `BatchPdfMerger.toPdfBytes`. Теперь ScoA-1 принтеры печатают любые форматы, не только PDF.
- ✅ 15.A.2 [`e1cc6db`] — точные сообщения для CAPT: 3 новых string-ресурса (`usb_capt2_in_development` для LBP-6000/6020/6030/7010C, `usb_capt_unknown_model` для не в matrix, `usb_capt_scoa1_non_pdf` для ScoA-1+non-PDF). EN+RU локализация.
- ✅ 15.A.3 [`85bbe47`] — `docs/capt2-snoop.md`: 9 тестовых сценариев + 3 пути захвата трафика + checklist готовности к 15.B.
- ✅ 15.E.1 [`e88c999`] — починены 11 deprecated warnings: AutoMirrored иконки, LocalLifecycleOwner перенесён в lifecycle-runtime-compose (новая зависимость), Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable), BluetoothGatt.writeDescriptor(d,v) на API 33+ с fallback. 0 warnings.
- ✅ 15.E.2 [`0e64c68`] — **реальный crash-fix** для Android 6/7: EmlToPdfRenderer использовал `java.util.Base64.getMimeDecoder()`/`getDecoder()` (API 26+), при minSdk=23 на устаревших устройствах падал с NoSuchMethodError при парсинге email. Миграция на `android.util.Base64`.
- ✅ 15.E final [`4dd74a4`] — bump 0.21.0→0.22.0, `STATUS_PHASE15_PARTIAL.md`.
- ✅ 15.E.3 [`63a0468`] — ручной аудит 6103 строк UI-кода (11 файлов) через Explore-агента + личная верификация. Заменено 5 теоретических race `!!` на `?.let` (PrintPreviewActivity x2, FilesScreen x2, PrintersScreen x1). Большинство пунктов отчёта Explore — ложные срабатывания (val в remember корректны). Backlog: runBlocking в BleGattPrintTransport.connect/writeAll — anti-pattern, ждёт BLE-принтер для рефакторинга.
- APK 0.22.0-debug положен в `~/Downloads/Projects/CebersPrint/CebersPrint_phase15_partial_v0.22.0_app-debug.apk` (21 MB) + STATUS-doc рядом.

**КАРДИНАЛЬНЫЙ ПОВОРОТ — 15.F: native captdriver port (2026-05-20, версия 0.23.0):**

Юзер согласовал GPL-3 путь для скорости. Вместо clean-room реверса (недели) — портируем существующий **captdriver (mounaiban fork, GPL-3)** который **уже поддерживает LBP6000/LBP6018** (EXPERIMENTAL, готовый encoder в `prn_lbp2900.c::lbp6000_ops`).

**Стек:**
- NDK 26.1.10909125 + CMake 3.22.1 поставлены через sdkmanager в `~/Tools/android-sdk`.
- 17 файлов captdriver скопированы в `app/src/main/cpp/captdriver/` (1681 LOC).
- `capt-command.c` полностью переписан — CUPS sidechannel → JNI callbacks через `io_provider.h` (5 точек I/O локализованы в одном файле, остальные модули as-is).
- `cups_compat.h` — минимальная `struct cups_page_header2_s` для отвязки от libcups.
- `capt_jni.c` — JNI entry с `setjmp/longjmp` для fatal-error escape + `jni_write_thunk/jni_read_thunk` для C→Kotlin callbacks.
- `CaptDriverNative.kt` — Kotlin-обёртка с `nativeProbeModel` (proof-of-concept) + `jniWrite/jniRead` для USB bulk-transfer.
- Build: 4 ABI (arm64-v8a, armeabi-v7a, x86_64, x86), `libcaptdriver_jni.so` 19-31 KB на ABI, APK 25 MB.

**Прогресс 15.F:**
- ✅ 15.F.1 [`0234ce5` сценка] — captdriver upstream проанализирован, LBP6000 supported в `prn_lbp2900.c::lbp6000_ops`
- ✅ 15.F.2 [`0234ce5`] — NDK cross-compile работает на все 4 ABI, libcaptdriver_jni.so в APK (19-31 KB)
- ✅ 15.F.3 [`91c0ca0`] — полный nativeRunJob цикл в capt_jni.c (do_print из памяти, без CUPS raster), printPdf+packMsbBlack в CaptDriverNative.kt, интеграция в UsbPrintTransport CAPT-ветку. **Старый CaptTransport.kt теперь dead code** (оставлен на всякий случай в `data/usb/capt/`).
- ✅ 15.F.4 [`91c0ca0`] — LICENSE.GPL3 в корень репо, THIRD_PARTY_LICENSES.md обновлён (captdriver native bundled, whole-binary GPL-3 since v0.24.0), README license line обновлена.
- ⏳ 15.D — отладка на железе LBP6000. Без принтера не проверено.

**Лицензия:** v0.24.0 — **whole-binary GPL-3 or later** (потому что `libcaptdriver_jni.so` статически линкуется в APK). Юзер согласовал.

**APK артефакты в `~/Downloads/Projects/CebersPrint/`:**
- `CebersPrint_phase15_partial_v0.22.0_app-debug.apk` (21 MB) — без native
- `CebersPrint_phase15_native_v0.23.0_app-debug.apk` (25 MB) — с native, proof-of-concept (только nativeProbeModel)
- `CebersPrint_phase15_native_v0.24.0_app-debug.apk` (25 MB) — **production-ready**, полный runJob+CAPT интеграция, ждёт принтер для физических тестов

**Лимит:** на устройстве не тестировано (нет принтера). Все compile-time гарантии есть: 192/192 unit-тестов, .so для 4 ABI, JNI signatures совпадают, lifecycle reflects rastertocapt.c::do_print. Реально печатать должно — но без принтера невозможно гарантировать корректность packMsbBlack (порядок битов) и параметров page header (CUPS values установлены heuristically через PageSize=A4 mm→points, Margins=0).

**Что вероятно потребует подкрутки на железе:**
1. packMsbBlack — порядок бит и направление (left-to-right MSB vs right-to-left). Если страница выходит зеркальная — поправить in-place.
2. cups_page_header2_s поля: HWResolution, PageSize, Margins. Можно сравнить логи captdriver с эталонной печатью через CUPS на mac mini (если будет нужно).
3. Возможно нужны bidirectional control transfers для USB Printer Class (status read), но captdriver работает только через bulk endpoints — в норме хватает.

Когда юзер подключит LBP6000:
1. Прошить APK 0.25.0
2. Подключить принтер по OTG
3. Попробовать печать тестовой страницы из приложения
4. Если не работает — смотреть `adb logcat | grep captdriver` для диагностики
5. Корректировать по findings.

**15.G — smoke test + UI-фиксы (2026-05-20, v0.25.0):**

AVD `cebersprint_test` (Pixel 5, Android 14 ARM64, headless) поставлен через sdkmanager+avdmanager. После прогона APK 0.24.0 нашёл и зафиксил 3 UI-бага (коммит `a85d6f4`):

1. **PrintersScreen discovery row** — `FilledTonalButton(weight=1f)` для USB/Wi-Fi/Bluetooth, "Bluetooth" не помещался → переносился на 2 строки. Фикс: `horizontalScroll` + убрал `weight` + `maxLines=1`.
2. **ToolsScreen ToolPicker** — 5 `Button(weight=1f)` для QR/Barcode/WebURL/Receipt/Text, на Pixel 5 ширине каждый символ переносился на новую строку (катастрофа). Тот же фикс.
3. **ScanScreen colour chips** показывали `enum.name` (COLOR / GRAYSCALE / BLACK_AND_WHITE), нечитаемый. Добавил `ColorMode.displayLabel()` → "Color / Grayscale / B/W".

Также `maxLines=1, softWrap=false` на PrintersScreen filter chips для защиты от длинных переводов.

**Dead code обнаружено:** `StatsScreen.kt` никем не вызывается — реальный stats показывается через `StatsOverlay` из JobsScreen toolbar. Не удалил (возможно есть план перенести в bottom nav).

**Smoke test pass для 6 экранов** (Printers/Files/Jobs/Tools/Scan/Settings): UI рендерится, навигация работает, **никаких crashes/exceptions в logcat**. Add printer dialog (FAB +) тоже работает.

**APK 0.25.0:** `~/Downloads/Projects/CebersPrint/CebersPrint_phase15_native_v0.25.0_ui_fixes_app-debug.apk` (25 MB) — финальный для текущей сессии без принтера.

**Что осталось / backlog без принтера:**
- Расширение unit-тестов на топ-10 untested модулей (DocxToPdfRenderer 657 LOC, PwgRasterEncoder/UrfEncoder binary round-trips, etc.) — отдельная сессия
- AirPrint `_universal._sub._ipp._tcp` через JmDNS — требует iPhone/Mac для проверки
- Cloud backup Drive/WebDAV — требует endpoint+creds
- runBlocking в BleGattPrintTransport — anti-pattern, требует BLE-устройство

**Эмулятор остановлен** для освобождения RAM. Перезапуск: `~/Tools/android-sdk/emulator/emulator -avd cebersprint_test -no-window -no-audio -no-boot-anim -gpu swiftshader_indirect -no-snapshot &` (нужен `source ~/.cebersprint_env`).
- ✅ Toolchain (JDK 17 Temurin + Android SDK 34 + build-tools + platform-tools) поставлен в `~/Tools/jdk-17.0.19+10` и `~/Tools/android-sdk` на mac mini. Env-файл `~/.cebersprint_env`.
- ✅ Финальная сборка: APK 20 MB `app/build/outputs/apk/debug/app-debug.apk`, **192/192 unit-тестов pass**, 0 deprecation warnings.

**Существующий `docs/nokoprint_analysis.md`** (важно для 15.B/C): NokoPrint v4.1.0 декомпилирован, обнаружено что они используют **Gutenprint/HPLIP/SpliX** через JNI (`DrvWrapper_*`). Native .so файлы НЕ в base APK — поставляются через split delivery. То есть просто декомпиляция NokoPrint нам не дала бы код CAPT-2; реверс протокола на проводе — единственный путь.

**Заблокировано до получения принтера:** 15.B (snoop), 15.C (encoder, без знания протокола), 15.D (отладка). 15.E частично доступен (deprecated warnings, AirPrint subtype).

**Deprecated warnings найденные в baseline сборке (на 15.E):** BLE GATT `value/writeDescriptor` (deprecated в Android 13), `LocalLifecycleOwner` (нужен `androidx.lifecycle.compose`), `Icons.Filled.LibraryBooks/Sort/ArrowBack` (AutoMirrored variants), `Modifier.menuAnchor` (нужна перегрузка с MenuAnchorType).

См. также [[mac-mini-upterm-access]] для доступа.
