Недавно меня опять заклампили. Я живу в Европе, и здесь вместо штрафов за неправильную парковку и эвакуаторов “клампят” — заковывают колесо твоего автомобиля в цепи. Чтобы выбраться, нужно звонить по телефону, платить круглую сумму и ждать мужика с ключами, который снимет цепь. Это долго, унизительно и порой (зависит от района) грабительски дорого.

В тот день я опоздал везде. Ожидая звенящего ключами работника, я размышлял, насколько глупо попался. Забегался, оставил машину на полчаса вместо максимальных бесплатных 20 минут — ровно на 21-й минуте и попался. Не повезло, полосатый фургончик парковщиков стоял недалеко, и они моментально среагировали. Ловили меня и до этого, по разным причинам: забывал, истекал оплаченный срок, а иногда и просто не мог найти свою машину в лабиринте улиц.

“Для всего должно быть приложение” — подумал я и начал копаться в апп сторе. После вороха сомнительных результатов у меня поубавилось уверенности, и я решил уточнить: “для всего должно быть приложение на андроид”. После чего нашел свой хуавей и полез в недра плей стора. Оттуда на меня высыпалось еще больше мусора, и я, утопая в корявых поделках, плюнул. Либо я ищу как-то не так, либо не существует удобного и понятного трекера парковки. Вывод простой: если у нас чего-то нет, давайте сделаем это сами.

На фоне у меня уже пилился долгострой, отнимавший почти все свободное время. Я решил взять паузу и в промежутке собрать другой проектик. Прикинув желаемый функционал, мне представился срок в месяц-полтора, и, забегая вперед, скажу, что в принципе вышло даже быстрее. По итогу получилось компактное и чистое приложение на обе платформы, очень удобное. Сейчас сам им пользуюсь и предлагаю попробовать вам.

image

Идея


Итак, чего же я хотел бы от трекера парковки? Чего мне всегда не хватает, когда я попадаю на штраф? Подумав, я сформулировал следующие вводные:

  1. Простое. В идеале однокнопочное. Минимум отвлекающих функций: никаких профилей, истории парковок, поддержки нескольких авто одновременно и тд.
  2. С уведомлениями. Я бы не забывал про машину, если бы приложение мне напоминало: братан, у тебя там парковка между прочим, и она вот-вот истечет.
  3. Учитывать время ходьбы. Это простейшая мысль, которую я нигде не видел воплощенной. Мне не надо знать, что парковка истечет через 5 минут, если я в получасе ходьбы от нее. Мне нужно напомнить так, чтобы я успел дойти до машины за пару минут до конца и уехать.
  4. Маршрут и навигация к парковке. Навигация, естественно, через гугл или эппл карты, а вот маршрут можно и в своем приложении показывать. Это поможет, если ты оставил машину в незнакомом районе, таймер тикает, а ты бегаешь кругами по одинаковым улицам.

В качестве технологической базы я выбрал Flutter, так как уже работаю с ним какое-то время и считаю неплохим. Для маршрутов решил использовать HERE API, тоже уже не в первый раз, а для карты — Mapbox. Набросал себе несколько вариантов интерфейса, выбрал самый минималистичный и включился в работу.

Разработка


В принципе, про сам процесс разработки рассказать можно не особо много. Серверной части тут нет, простейший клиент, состоящий по сути из одной большой вьюшки в разных состояниях. На первой странице ты либо выбираешь пресет — 1, 2, 4, 6 часов — либо слайдером настраиваешь свой срок парковки. Попробовав это вживую, я понял, что слайдер достаточно груб, и добавил ему кнопки “+” и “-“. Забавным и довольно неочевидным нюансом тут стало то, что, пока ты выбираешь — время идет. Так что обновлять эту вьюшку нужно в реальном времени. Выбрав срок, нажимаем на единственную кнопку внизу, и данные по локации и времени сохраняются, а парковка стартует. Важно заметить, что я принципиально храню все данные на борту, ничего ни на какой сервер не передается. В дальнейшем это, кстати, создаст мне трудности, но про это позже.

Вторая страница — это, собственно, таймер парковки. Я перепробовал тут кучу вариантов: начинал с дико перегруженных инфой прототипов, после чего итеративно избавлялся и упрощал до предела. Мне очень хотелось, чтобы статус парковки изображался визуально, а не сухими цифрами, поэтому главным элементом этой страницы наряду с картой стала кольцевая диаграмма. Она показывает истекшее время, время ходьбы и оставшееся время. Если ты находишься не рядом с машиной и время ходьбы ненулевое — появляется кнопка запуска навигации, а карта показывает примерный маршрут до парковки. Вот, собственно, и все.

Не смотря на визуальную простоту, этот экран изрядно потрепал мне нервы. Надо понимать, что пока парковка активна, происходит одновременно ряд процессов:

  • время идет, и истекает таймер
  • ты перемещаешься, и время пути меняется

Все это происходит в реальном времени и должно моментально отображаться на интерфейсе. Диаграмма должна плавно перестраиваться, карта двигаться и перерисовывать маршрут, при риске опоздать или истечении парковки соответствующее предупреждение тоже должно выскакивать. Более того: при закрытии и открытии приложения нужно это все корректно восстановить. И наконец страшное: весь функционал должен уметь работать в фоновом режиме, ведь большинство своей жизни приложение проведет именно на фоне. Такие дела.

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

С остальным вроде вышло все гладко. Собственно, остального там было и немного: пара диалогов, да интро-страница, для которой моя девушка (она художница) набросала приятные анимации. Наконец, я перевел все приложение на русский, включая карту. А вот дальше началась боль: уведомления.

Уведомления


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

  1. Постоянно мониторило твою геолокацию,
  2. Проверяло, сколько минут ходьбы до парковки
  3. Сверялось с истекающим таймером
  4. Если есть риск не успеть вернуться — стреляло уведомление

Чувствуете масштаб трагедии? Понятно, что не существует технической возможности это делать, если приложение совсем убито. Но допустим, оно просто свернуто, а экран выключен. Маршрут и оценку времени ходьбы мне дает апи HERE, значит ли это, что приложение будет спамить реквестами на фоне? Сколько кода вообще будет при этом прогоняться? Как у нас с памятью и расходом энергии?

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

Ну хорошо, мы написали легкий код, который умеет прогоняться на фоне и управляется таймером. Как нам его тригерить в рамках нативных приложений и передавать туда данные геолокации? Оказалось, по-разному.

Логика андроида и айфона в вопросах выполнения кода на бекграунде кардинально разная. Для айфона оно отсутствует как таковое: фоновый код может вызвать только системное событие определенного рода, например проигрывание потокового аудио или — к счастью для меня — обновление локации. Для этого достаточно запросить пермишен и в нативной части флатеровского плагина подписаться на событие. Оттуда через специфический канал связи (MethodChannel) мы вызываем коллбэк в дарте, передаем ему геоданные и запускаем фоном нужную нам логику. Красота.

У андроида это работает по-другому. Технически у него есть возможность выполнять код фоном без привязки к системным событиям — для этого надо описать сервис. Он висит отдельно от приложения либо совсем на фоне, либо на форграунде (там он дает от себе знать прикрепленным сообщением в шторке). Казалось бы, проблема решена: в нативной части плагина описываем фоновый сервис, который трекит локацию и дергает дартовский код. Однако есть нюанс. С андроида версии 8.0 поменялось отношение к таким сервисам: теперь, если приложение свернуто в данный момент, его фоновые сервисы адски режутся осью в целях экономии энергии. И, если твой код осуществляет, например, бекап данных, то тебе в принципе все равно, в какой именно момент ось пустит его работать — главное, чтобы сработал. А вот в моем случае нам необходимо отслеживать изменение локации практически в реальном времени, и тайминг здесь критичен. Поэтому единственный выход, это пускать сервис в форграунд. Описываем небольшое прикрепленное в шторке уведомление, подписываемся на локацию, через MethodChannel передаем ее в дарт. Вроде все работает.

Кстати, возвращаясь к вопросам памяти, расхода энергии и количества реквестов — код прогоняется при смещении локации в 50 метров, либо по таймеру. Причем сам он очень легкий, а реквесты проходят не через меня, а идут напрямую на апи HERE. Я сам не получаю никаких данных и нигде у себя не сохраняю. Вообще подобная реализация очень хорошо описана тут, это блог одного из инженеров флаттера — там и код прилагается. Его пример несколько про другое, но общий подход понятен, так что рекомендую почитать.

Результат


Вот, собственно, и все. За месяц свободного времени у меня получилось приложение, отлично решающее конкретную задачу. Надеюсь, эта статья будет кому-то интересна, а приложение спасет от штрафов и стресса. Поглядеть и поругать можно тут: Android, iOS.

Всех благодарю за внимание.

Комментарии (34)


  1. SemenPV
    20.08.2019 21:25

    Бутылка царской водки в багажнике как бэкап?


    1. Assimilator
      21.08.2019 00:52

      Болгарка быстрее


      1. EGregor_IV
        21.08.2019 07:28

        Цепь проще клещами перекусить. Быстрее и бесшумнее.


        1. Yaris
          21.08.2019 09:03

          За это можно получить штраф ещё "круглее", чем за собственно просроченную парковку. В моей части Европы такое очень быстро эскалируется до "запрета на движение автомобиля" (не знаю, как это по-русски называется) — когда машине просто нельзя появляться на дороге иначе, чем на эвакуаторе, а не то конфискация.


          1. dchabanenko Автор
            21.08.2019 12:13

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

            Но это конечно все глупости, я до кусачек в багажнике пока не дошел — пытаюсь не нарушать)


            1. stantum
              21.08.2019 19:07

              Скорее всего у полиции/парковщиков ведется журнал, кому и когда нацепили. И он будет считаться более достоверным источником, чем заявление о «ничего не знаю».


              1. Assimilator
                22.08.2019 21:20

                Если уже таскаешь болгарку/клещи в багажнике, то наверное нужно ставить фейковый номерной знак поверх своего. Парковщики один фиг пробивать не будут, только запишут.


        1. shifttstas
          21.08.2019 09:34

          Т.е вариант не нарушать — вообще не рассматривается?


          1. pilniy
            21.08.2019 12:15

            не рассматривается. вы всегда выполняете все, что придумал какой-то хрен с горы?


        1. iig
          21.08.2019 13:00

          Поставить запаску, колесо с цепью в багажник, дома неспешно решить вопрос с цепью?


  1. apple01
    20.08.2019 22:40

    Спасибо, мне понравилась идея и реализация. Мне нужно аналогичное приложение которое мониторит позиции меня и автобуса с GPS тракингом и сообщает когда пора выходить из дома. Причем оно должно издавать периодические звуковые сигналы, частота которых увеличивается если мне нужно ускориться чтобы успеть на остановку. В установках нужно добавить интервалы безопасности чтобы прибыть на остановку на пару минут раньше. Мечтаю о таком уже несколько лет :) На самом деле это частный случай общей задачи.


    1. KEugene
      21.08.2019 05:28

      Может, это зависит от региона, но подобный функционал у гугл мапс: выйти через 15 минут, чтобы подойти к остановке к приезду автобуса маршрута такого-то.


      1. apple01
        21.08.2019 17:46

        Протестировал гугл мапс. У меня по крайней мере он опирается на расписание а не на трекинг к сожалению.


    1. MotoDruG
      21.08.2019 11:56

      им нельзя слепо верить(( иногда трекер подвисает, уже бывало так, смотришь на карту, маршрутка там, по прикидкам будет через 5 минут, ждёшь, через 5 минут опять смотришь, маршрутка уже за 5 остановок уехала, делаешь круглые глаза «как так? она же мимо не проезжала». Только потом начал подмечать, что некоторые маршруты практически лайф-вьюв, а некоторые если пару минут стоят на одном месте — 100% зависли.


      1. dchabanenko Автор
        21.08.2019 12:16

        Насколько я знаю, они используют GTFS (General Transit Feed Specification), и не все компании предоставляют реалтаймовый фид. Для многих это тупо статичная табличка время-станция, вот гуглы и пытаются прикинуть, а реальное время конкретного автобуса другое — он уже уехал или не приехал


      1. apple01
        21.08.2019 17:49

        В моем случае есть real time и автобусная компания предоставляет простую текстовую страницу для мобильного телефона с обратным отсчетом времени для выбранной остановки.
        Слепо верить конечно нельзя, например автобус может вообще не появиться на трекинге, но это не значит что он обязательно не приедет. Но если появился и отсчет идет, то будет обязательно.


  1. KEugene
    21.08.2019 05:34

    На всякий случай хотел бы заметить, что постоянный пересмотр маршрута будет довольно энергоемким. Может, его перепрокладывать если телефон сменил позицию в радиусе, например, 10-50 м от предыдущего положения?


    1. dchabanenko Автор
      21.08.2019 12:19

      А я так и делаю — маршрут пересматривается при смещении в 50 метров. Там у меня есть хитрости в том, как код написан и запускается, потому что нужно таймер учитывать. Но реквесты на маршрут стреляют только при крупном смещении


      1. slava_k
        24.08.2019 09:33

        Возможно имеет смысл для экстренных ситуаций с пропаданием связи/gps сделать шагомер по встроенному гироскопу. В фоновом процессе всегда начинать счет шагов и среднюю скорость с пройденным растоянием, и в случае длительной неработоспособности связи/gps примерно вычислять время на возврат.

        Также можно сделать простое серверное API для сбора данных треков и при длительном использовании приложения сравнивать предсказания времени и фактического результата для треков с общей зоной, при расхождениях в будущем вносить корректировки по времени. Это может быть актуально при перемещении через сложные пешеходные маршруты.


  1. artforteam2018
    21.08.2019 12:21

    Исходный код бы… Писал приложение на flutter и очень застрял на реализации фоновой работы для обоих платформ. Получилось даже, но потом все же перешли на сервер. Пример такого кода только один стоящий можно найти и он указан в статье.


  1. sw0rd
    21.08.2019 12:22

    del


  1. mOlind
    21.08.2019 15:03

    Все хорошо. Но как быть если интернет пропал? Сразу говорить пользователю чтобы шел к машине? Или вы зашли в здание и GPS уплыл в соседний район. У вас ломается предсказание, сколько идти обратно и не ясно в какую сторону. Может начать думать, что машина рядом, а может наоборот.


    1. dchabanenko Автор
      21.08.2019 15:15

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

      Было бы идеально считать маршрут в офлайне на борту, но это совсем другая лига...)


      1. mOlind
        21.08.2019 15:30

        Я бы считал сколько времени человек идет от последнего нормального локейшена/построеного маршрута и закладывал столько же времени на обратную дорогу.

        Офлайн навигация не так сложна, как может показаться. Но пользователю надо будет предварительно качать навигационные данные для своего города.


    1. nikolayv81
      21.08.2019 17:39

      В соседний район, это ладно, а вот в аэропорт за 20 км за пару секунд легко, и крупным игрокам (Яндекс, гугл) в голову не приходит, что превысить скорость света проблематично...


  1. dchabanenko Автор
    21.08.2019 15:10

    Всем, у кого покрашилось на андроиде 9 — уже починил и выкатил. Извините)

    Там теперь оказывается нужен отдельный FOREGROUND_SERVICE пермишен, а я не спросил


  1. spiceginger
    21.08.2019 15:23

    Хорошее приложение, отправил знакомым ирландцам так как сам не вожу.
    В ответ получил фидбэк, что они оплачивают парковку с приложения, оно присылает уведомление о том что время заканчивается. Да оно не говорит сколько идти, но говорят возиться с 2мя приложениями — морока.


    1. dchabanenko Автор
      21.08.2019 15:32

      Спасибо. Я знаю это приложение, с ним как раз и попадал, что присылают за 5 минут, а я черт знает где)

      Плюс есть же куча бесплатных на короткое время парковок. Там главное не опоздать в бесплатное окно


  1. Bedal
    21.08.2019 15:58

    Всё неплохо, вот только, когда ты ушёл от машины, то с высокой вероятностью будешь в помещении. GPS-координаты будут неадекватными. Ладно, если просто пропадут — а то ведь часто именно показывают невесть куда.
    ___
    Паrдон, не заметил, что mOlind уже о том же написал.


    1. dchabanenko Автор
      21.08.2019 16:10

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

      Но вообще конечно надо тестить


      1. Bedal
        21.08.2019 16:59

        боюсь, здесь это не очень получится, большинство читающих — москвичи, но в Москве можно просто заплатить (и доплатить) за парковку с телефона. Там другая проблема:

        главный подвох — заплатить за парковку с другим условным номером за углом, а за эту получить штраф

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

        Но вообще Вы поймали интересную задачку — при внешней простоте много всего нужно учитывать (те же вайфаи, предполагаемую скорость перемещения в разных местах...).


  1. Mingun
    21.08.2019 17:59

    Вы зачем так статью назвали? Уже который раз, листая ленту, ловлю себя на мысли, зачем и КАК он пакует людей и кому это надо отслеживать...


  1. psinetron
    21.08.2019 18:09

    А вот такой вопрос — наверняка при построении маршрута используется какой-нибудь google Routes. А у них большие ограничения на бесплатное использование. Каким сервисом в итоге маршрут строите?


    1. dchabanenko Автор
      21.08.2019 21:29

      HERE API — полет нормальный, не первый раз пользуюсь