Как я делал трекер парковки для людей

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

    В тот день я опоздал везде. Ожидая звенящего ключами работника, я размышлял, насколько глупо попался. Забегался, оставил машину на полчаса вместо максимальных бесплатных 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.

    Всех благодарю за внимание.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

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

    • НЛО прилетело и опубликовало эту надпись здесь
        0
        Болгарка быстрее
          –1
          Цепь проще клещами перекусить. Быстрее и бесшумнее.
            0

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

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

              Но это конечно все глупости, я до кусачек в багажнике пока не дошел — пытаюсь не нарушать)
                0
                Скорее всего у полиции/парковщиков ведется журнал, кому и когда нацепили. И он будет считаться более достоверным источником, чем заявление о «ничего не знаю».
                  0
                  Если уже таскаешь болгарку/клещи в багажнике, то наверное нужно ставить фейковый номерной знак поверх своего. Парковщики один фиг пробивать не будут, только запишут.
              0

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

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

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

                0
                Протестировал гугл мапс. У меня по крайней мере он опирается на расписание а не на трекинг к сожалению.
                0
                им нельзя слепо верить(( иногда трекер подвисает, уже бывало так, смотришь на карту, маршрутка там, по прикидкам будет через 5 минут, ждёшь, через 5 минут опять смотришь, маршрутка уже за 5 остановок уехала, делаешь круглые глаза «как так? она же мимо не проезжала». Только потом начал подмечать, что некоторые маршруты практически лайф-вьюв, а некоторые если пару минут стоят на одном месте — 100% зависли.
                  0
                  Насколько я знаю, они используют GTFS (General Transit Feed Specification), и не все компании предоставляют реалтаймовый фид. Для многих это тупо статичная табличка время-станция, вот гуглы и пытаются прикинуть, а реальное время конкретного автобуса другое — он уже уехал или не приехал
                    0
                    В моем случае есть real time и автобусная компания предоставляет простую текстовую страницу для мобильного телефона с обратным отсчетом времени для выбранной остановки.
                    Слепо верить конечно нельзя, например автобус может вообще не появиться на трекинге, но это не значит что он обязательно не приедет. Но если появился и отсчет идет, то будет обязательно.
                  0

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

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

                      Также можно сделать простое серверное API для сбора данных треков и при длительном использовании приложения сравнивать предсказания времени и фактического результата для треков с общей зоной, при расхождениях в будущем вносить корректировки по времени. Это может быть актуально при перемещении через сложные пешеходные маршруты.
                    +1
                    Исходный код бы… Писал приложение на flutter и очень застрял на реализации фоновой работы для обоих платформ. Получилось даже, но потом все же перешли на сервер. Пример такого кода только один стоящий можно найти и он указан в статье.
                      0
                      del
                        0
                        Все хорошо. Но как быть если интернет пропал? Сразу говорить пользователю чтобы шел к машине? Или вы зашли в здание и GPS уплыл в соседний район. У вас ломается предсказание, сколько идти обратно и не ясно в какую сторону. Может начать думать, что машина рядом, а может наоборот.
                          0
                          Угу, я думал об этом. Сейчас, если нет связи, работает просто таймер — все равно придет предупреждение, правда без учета ходьбы. И при каждом смещении апп попробует получить маршрут.

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

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

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

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

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

                            А, еще: в настройках нужно иметь включенным «Более точное определение местоположения» (или «Improve Location Accuracy»). Они в девятке это тоже поменяли, и теперь эта функция подглючивает
                              +1
                              Хорошее приложение, отправил знакомым ирландцам так как сам не вожу.
                              В ответ получил фидбэк, что они оплачивают парковку с приложения, оно присылает уведомление о том что время заканчивается. Да оно не говорит сколько идти, но говорят возиться с 2мя приложениями — морока.
                                –1
                                Спасибо. Я знаю это приложение, с ним как раз и попадал, что присылают за 5 минут, а я черт знает где)

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

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

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

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

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

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

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

                                    Самое читаемое