← Все проекты

Android · без интернета

Гараж: сервисная книжка автомобиля

Журнал обслуживания, план работ по пробегу и календарю, фотографии полиса, СТС и прав. Разрешения выходить в сеть у приложения нет вовсе, поэтому снимки документов физически не могут никуда уйти, а напоминания считаются на самом телефоне.

Kotlin Jetpack Compose Room AlarmManager AES-GCM Material 3

Отладочная сборка для Android

Версия 0.1.0 · 11 МБ · от 30.08.2026

Войти и скачать

Файлы проектов доступны после входа. Регистрация занимает минуту и нужна затем, чтобы я знал, у кого на руках сборка: это отладочные версии, и о найденных ошибках есть кому написать.

Сборка отладочная и подписана отладочным ключом: Android спросит разрешение на установку из неизвестного источника. В магазины такая сборка не выкладывается.

Зачем это нужно

История обслуживания машины живёт в трёх местах сразу: пачка чеков в бардачке, переписка с мастером и память владельца. К моменту продажи выясняется, что подтвердить нечем ничего — ни когда меняли ремень ГРМ, ни какое масло залито. А полис ОСАГО заканчивается ровно в тот день, когда о нём никто не помнит.

Приложение собирает это в одну сервисную книжку: что сделано, на каком пробеге и во сколько обошлось. Оно же считает, когда подойдёт следующий срок — по календарю и по километрам одновременно, — и напоминает заранее. Фотографии документов лежат рядом, в самом приложении.

Главное решение: сети нет

В приложении хранятся снимки водительского удостоверения, СТС и полиса. Это ровно те документы, утечка которых стоит дороже всего, и обещания «мы бережно относимся к вашим данным» тут мало. Поэтому разрешения на выход в сеть у приложения нет вовсе:

<!-- android.permission.INTERNET в манифесте отсутствует -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

Без этого разрешения система физически не даёт приложению открыть сокет. Проверяется за десять секунд: включите авиарежим — всё продолжает работать. Разрешений на камеру и на чтение галереи тоже нет: снимок делает системная камера, а файл из галереи отдаёт системный выбор фотографий, который передаёт ровно один выбранный файл.

Отдельно выключено автоматическое резервное копирование Android. По умолчанию оно выгружает приватную папку приложения в Google Drive владельца телефона — то есть документы уехали бы в облако мимо отсутствующего разрешения, тихо и без спроса.

Четыре экрана

01

Машина

то, ради чего открывают

Пробег крупными цифрами и рядом — откуда он взялся: замер это или расчёт и когда был последний замер. Ниже то, что горит, что скоро, расходы за год и стоимость километра.

02

Журнал

сервисная книжка

Что сделано, когда, на каком пробеге, в каком сервисе, что залито и поставлено, во что обошлось. Сгруппирован по месяцам с суммой за каждый, ищется по деталям и сервисам.

03

План

что и когда сделать

Срок задаётся датой, пробегом или обоими сразу. Порядок в списке — по срочности, а не по дате создания. Кнопка «Сделано» превращает план в запись журнала и сама ставит следующий срок.

04

Документы

полис, СТС, права

Фотографии, номера, сроки. Документы машины и документы водителя разделены: права видны при любом выбранном автомобиле, потому что принадлежат человеку, а не машине.

Как приложение узнаёт пробег

Телефон к машине не подключён и одометр не видит. Всё, что есть, — показания, которые человек ввёл сам, и заезды в сервис, где пробег записан вместе с работой. По ним считается среднесуточный пробег, и уже он переводит «через 4 300 км» в дату:

Масло ДВС и фильтр
Осталось 10 000 км · срок 30 августа 2027 — ещё 365 дней
Примерно к 3 декабря

Без этого перевода напоминание по километрам невозможно вовсе: наступление порога замечалось бы только в тот момент, когда человек сам зашёл и обновил цифру, — то есть заведомо поздно.

Обратная сторона в том, что расчёт тем хуже, чем дольше не обновляли пробег, и приложение об этом говорит прямо. Оно всегда показывает, на чём основана цифра: замер это или оценка, посчитана скорость по вашей истории или взята из настройки «сколько проезжаю за год». Если пробег не уточняли больше двух месяцев, на карточке появляется об этом строка, а раз в месяц приходит напоминание.

Приложение, которое выдаёт догадку за факт, теряет доверие ровно один раз — и вместе с ним теряется смысл всех остальных его подсказок.

Решения внутри

«Или год, что наступит раньше»

Регламенты написаны двумя величинами сразу, и упрощать это до одной нельзя: у машины, которая стоит в гараже, масло стареет по времени, а у таксиста — по километрам. У плана есть и дата, и пробег, обе необязательные; пробег переводится в дату через прогноз, и дальше сравниваются даты. У каждого срока свой запас — «напомнить за две недели» и «напомнить за 500 км».

Напоминания без сервера и Firebase

Пуш в обычном смысле требует сети, которой у приложения нет. Раз в сутки будильник будит приложение, оно сверяет планы и документы с пробегом и календарём и показывает уведомление. У каждого напоминания есть ключ идемпотентности: будильник, сработавший дважды после перезагрузки телефона, не покажет одно и то же второй раз.

Почему AlarmManager, а не WorkManager

WorkManager был бы уместен, будь задача «раз в сутки когда-нибудь». Но напоминание должно приходить в понятный час, а периодическая работа привязана к моменту запуска, а не к стрелкам часов: после перезагрузки в три ночи проверка так и осталась бы ночной. Будильник при этом неточный — точный требует разрешения, которое выдают будильникам и календарям, а не напоминаниям про масло.

Резервная копия — один зашифрованный файл

Раз автоматического копирования нет, перенос на новый телефон делается вручную: всё содержимое складывается в ZIP, ZIP шифруется паролем через AES-256-GCM, место для файла выбирает человек. Незашифрованный архив с фотографией ПТС в папке «Загрузки» — ровно та утечка, ради предотвращения которой в приложении нет сети. GCM заодно проверяет целостность: испорченный файл не «частично распакуется».

Никаких сетевых библиотек в зависимостях

Coil для показа картинок не подключён: он тянет за собой OkHttp, то есть сетевой стек внутрь приложения, чьё главное обещание — что оно в сеть не ходит. Нужного от библиотеки ровно два: не читать снимок в память целиком и не декодировать один файл на каждой перерисовке списка. Оба пункта уместились в шестьдесят строк.

Иконки нарисованы руками

Набор material-icons-extended объявляет каждую иконку функцией, и в отладочной сборке, которая не проходит через оптимизатор, в приложение попадают все две с половиной тысячи. Пяти недостающих значков хватило нарисовать самому: APK похудел с 17,4 до 10,9 МБ.

Границы

Это первая версия, и часть работы оставлена на следующий этап — осознанно, а не по забывчивости.

  • Синхронизации между телефонами нет. Перенос — экспортом в зашифрованный файл. Настоящая синхронизация требует сервера, а с ним и всего, от чего проект отказался.
  • Учёта заправок и расхода топлива нет. Вид работы «Заправка» в справочнике есть, но графика «литров на сотню» — нет: он требует отмечать полный бак и различать частичные заправки, то есть отдельного экрана со своей логикой.
  • Номер полиса вводится руками. Офлайн-распознавание печатного текста — отдельная работа с моделью на десятки мегабайт.
  • Экспорта журнала в PDF для покупателя нет. История в приложении есть, отдать её одним файлом пока нельзя.

Сборка ниже отладочная и подписана отладочным ключом. Учётной записи она не спрашивает и данных никуда не отправляет: гараж заводится сразу после установки.

Нужно похожее?

Опишите задачу в двух строках — отвечу в течение рабочего дня. Исходный код этого проекта и демо покажу по запросу.

Обсудить задачу Другие проекты