Создание POS-приложения для Android, которое нельзя закрыть
Опубликовано: 2022-03-11Мир разработки мобильных приложений огромен и постоянно развивается, новые фреймворки и технологии появляются почти ежедневно. Когда вы думаете о мобильном устройстве, вы, вероятно, думаете о своем телефоне или, возможно, планшете, хотя они и близко не так популярны, как смартфоны.
iOS от Apple и Android от Google доминируют на рынке мобильных устройств, и у каждого из них за последнее десятилетие были свои взлеты и падения. Сегодня я собираюсь больше поговорить об Android и его использовании на устройствах, которые не обязательно являются мобильными.
Открытый исходный код имел очень интересный побочный эффект на мобильную операционную систему Google. Конечно, мы можем думать обо всех различных ответвлениях Android от разных производителей смартфонов, но как насчет всех устройств под управлением Android, которые не являются мобильными? Все, начиная от холодильников, умных духовок, дверных замков и даже устройств для торговых точек (POS), в настоящее время может работать под управлением Android. Именно из-за последних я и написал эту статью.
POS-системы Android
Около года назад мне довелось поиграть с Android-устройством, которое было каким угодно, но только не обычным, и вряд ли большинство людей будут его использовать. Рассматриваемое устройство представляет собой POS-систему на базе Android от китайского производителя, которая также имеет встроенный термопринтер (наподобие тех, которые используются для распечатки чеков в магазинах или банкоматах).
Однако самым большим сюрпризом стало его программное обеспечение: оно работало под управлением базовой версии Android. Если я правильно помню, в то время он работал под управлением Android 8 или Android Oreo, если вы предпочитаете кодовые имена Google. Само устройство выглядит как портативное POS-устройство старой школы, но вместо физической клавиатуры, на которой вы вводите свой PIN-код, оно оснащено емкостным сенсорным экраном, подобным тем, которые использовались в телефонах Android прошлых лет.
Мое требование было простым: я должен был посмотреть, сможем ли мы использовать функции этого устройства, такие как термопринтер, одновременно запуская разрабатываемое нами приложение. Как только я понял, что само требование возможно, мое внимание привлекла еще одна проблема: безопасность .
Дело в том, что если у вас есть устройство, которое обрабатывает платежи по картам и другие виды транзакций, вы можете не захотеть, чтобы на этом же устройстве можно было запускать TikTok, Gmail или Snapchat. Это устройство вело себя точно так же, как планшет, и даже поставлялось с предустановленным Google Play Store. Представьте, что вы идете в небольшой магазин и видите, как ваш кассир делает селфи, открывает электронные письма нигерийского принца и просматривает странные веб-сайты, зараженные вредоносными программами.
И после этого кассир вручает вам то же устройство для ввода PIN-кода. Лично я бы не чувствовал себя в безопасности, предоставляя информацию о своей кредитной карте через такое устройство.
Блокировка доступа пользователей к меню Android
Помимо безопасности, мне пришлось взять на себя еще более важную задачу: мне нужно было заблокировать человека, использующего устройство Android POS, внутри моего приложения. Возиться с операционной системой было невозможно, поскольку эти устройства поставлялись нетехническим людям.
Конечно, кассиры более чем способны установить приложение, но большинство из них не могут прошивать пользовательские ПЗУ или выполнять другие операции более низкого уровня. Само приложение было написано на React Native, хотя в данном контексте это не имеет значения. Все изменения, которые я сделал, находятся в нативном коде Java, поэтому независимо от того, что вы используете для разработки своего основного приложения, эти настройки должны работать.
Небольшой отказ от ответственности: эта процедура работает только для приложений Android . Apple не дает нам контроля, необходимого для простого выполнения чего-то подобного на iPhone или iPad, что понятно, учитывая закрытую природу iOS.
Пользователь может выйти из приложения четырьмя способами:
- Используйте кнопку « Домой» .
- Используйте кнопку Назад .
- Используйте кнопку Недавние .
- Оставьте свое приложение через панель уведомлений .
Либо щелчок по недавнему уведомлению, либо переход к настройкам с этой панели приведет к выходу пользователя из нашего приложения. У вас также есть жесты, но, в конце концов, эти жесты вызывают те же самые действия, что и обычные нажатия кнопок.
Кроме того, наличие системы PIN-кода для разблокировки приложения может быть очень полезно для тех, кто управляет устройством. Таким образом, только тот, у кого есть PIN-код, сможет установить другую версию приложения, не предлагая более глубокого доступа конечному пользователю.
Кнопка «Домой»
Чтобы запретить пользователю нажимать кнопку «Домой», нам не нужно ее отключать.
Одной из полезных функций Android является наличие различных лаунчеров. Обычно эти приложения предоставляют вам различные домашние экраны, ящики приложений и доступ к различным настройкам пользовательского интерфейса. Каждое устройство Android имеет предустановленную производителем программу. В конечном счете, это обычные обычные приложения с одним небольшим, но важным исключением.
Это означает, что если бы операционная система могла распознать наше приложение как средство запуска, мы могли бы установить его в качестве средства запуска по умолчанию. Побочным эффектом этого является то, что каждый раз, когда вы нажимаете кнопку «Домой», устройство будет переводить вас в панель запуска «Домой». А если нашим приложением является лаунчер «Домой», то в принципе эта кнопка «Домой» становится бесполезной. Для этого нам нужно отредактировать XML-файл AndroidManifest в нашем проекте Android и добавить следующие две строки кода:
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
Первая строка сделает наше приложение доступным для выбора, если пользователь нажмет кнопку «Домой», а вторая строка позволит установить наше приложение по умолчанию всякий раз, когда это действие происходит.
Теперь осталось только, чтобы выездной агент установил приложение на устройство при доставке его клиенту. Всякий раз, когда вы устанавливаете приложение, которое потенциально может быть средством запуска, Android спросит вас, хотите ли вы использовать другое средство запуска и хотите ли вы установить его по умолчанию.
Итак, теперь, если вы нажмете кнопку «Домой» или очистите все свои последние приложения, устройство автоматически направит вас к моему приложению.
Кнопка «Назад»
Далее нам нужно обработать кнопку «Назад». Мобильные приложения обычно предоставляют экранный способ возврата между экранами, тем более что на многих устройствах нет специальной клавиши «назад».
Несколько лет назад это было в случае с устройствами iOS от Apple, которые отличались уже культовым дизайном с одной физической кнопкой под экраном. Однако в последние годы на большинстве устройств Android также отсутствуют физические кнопки «Домой». Сначала они перешли на экранные кнопки, а теперь мы видим, как они постепенно отказываются от телефонов в пользу жестов, поскольку производители телефонов переходят на полноэкранные устройства с небольшими рамками и подбородком.
Это означает, что кнопка «Назад», которую Android предоставляет по умолчанию, на самом деле не нужна, и чтобы сделать эту кнопку совершенно бесполезной, нам просто нужно добавить простой блок кода в нашу активность:
@Override public void onBackPressed() { }

Это довольно простой блок кода: наша основная активность позволяет нам перехватывать каждый раз, когда пользователь нажимает кнопку «Назад». В нашем случае, поскольку мы не хотим, чтобы пользователь нажимал эту кнопку слишком много раз, чтобы выйти из приложения, мы можем просто перезаписать метод по умолчанию тем, который ничего не делает, сообщая нашему приложению ничего не делать в случае, если Назад кнопка нажата.
Вот как некоторые приложения запрашивают подтверждение, прежде чем вы случайно выйдете из них, вернувшись назад слишком много раз.
Кнопка «Недавние»
Нам все еще нужно обработать кнопку «Недавние», и это самая сложная задача. Кроме того, это определенно не лучшая практика или что-то, что вы должны продвигать в Play Store, но здесь это работает для нашего нишевого случая.
Точно так же, как основное действие позволяет нам узнать, когда нажата кнопка «Назад», оно также позволяет нам узнать, когда приложение приостановлено. Что это значит? Этот код запускается каждый раз, когда наше приложение переключается с приложения переднего плана на фоновое.
При перехвате этого события мы получим идентификатор задачи нашего текущего приложения и скажем диспетчеру активности переместить эту задачу на передний план. Для этого нам нужно одно специальное разрешение в том же файле манифеста Android, который мы редактировали ранее.
<manifest xmlns:andro package="com.johnwick"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.REORDER_TASKS" /> <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> </application> </manifest>
Это позволит нам читать текущие задачи и вносить изменения в эти задачи. Также нам еще нужно перехватить момент, когда наше приложение уходит в фон. Мы снова можем переопределить метод onPause
в нашей активности.
Здесь мы получаем диспетчер задач и заставляем его переместить конкретную задачу на передний план. В нашем случае это та конкретная задача, которая только что была отправлена в фоновый режим (наше приложение).
@Override public void onPause() { super.onPause(); ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); activityManager.moveTaskToFront(getTaskId(), 0); }
Теперь каждый раз, когда вы хотите перейти в меню последних, приложение будет автоматически перефокусироваться. Конечно, иногда у вас может немного мерцать экран, но вы не сможете выйти из этого приложения. И вот еще одна крутая вещь — помните, как я говорил, что вы также можете выйти, нажав на уведомление или перейдя непосредственно в настройки через панель уведомлений? Что ж, выполнение этих действий переведет приложение в фоновый режим, который вызовет запуск нашего кода, а затем пользователь вернется обратно.
Все это происходит так быстро, что пользователь не заметит, что происходит на заднем плане. Кроме того, еще одна приятная особенность этого подхода заключается в том, что ваши быстрые переключатели по-прежнему доступны. Вы по-прежнему можете выбрать сеть Wi-Fi, например, или отключить звуки, но все, что требует от вас входа в фактическое приложение «Настройки», не разрешено.
Решение
Я не уверен, что это лучший способ сделать это, но, тем не менее, это был действительно интересный процесс исследования темы, о которой я даже не знал, что это возможно. И это работает! Предупреждение: на данный момент есть только два способа выйти из приложения как разработчика: либо переустановить операционную систему, либо убить/удалить приложение через ADB.
Если вы каким-то образом потеряете соединение ADB с устройством, я не знаю простого способа, который мог бы вас вытащить. Чтобы избежать этого, я создал систему PIN-кода.
Пограничные случаи
Есть несколько ситуаций, которые нам необходимо учитывать. Прежде всего, что делать, если устройство перезагружается? Это не обязательно должна быть ручная перезагрузка, это также может быть сбой операционной системы.
Поскольку ранее мы установили наше приложение в качестве средства запуска по умолчанию, как только операционная система загрузится, она должна автоматически запустить наше приложение. Но как Android узнает, что нужно загружать домашний экран при загрузке? Это потому, что он просто загружает ваш лаунчер по умолчанию. Поскольку на данный момент мы являемся средством запуска по умолчанию, перезагрузка не должна быть проблемой. И может ли Android убить наше приложение в какой-то момент? Теоретически это может убить приложение, если оперативная память переполнится, но в реальной жизни это практически невозможно. Поскольку наше приложение не закрывается, никто не может открыть другие приложения, поэтому оперативная память не должна заполняться.
Единственный способ, которым я могу думать о его заполнении, - это если в нашем приложении есть огромная утечка памяти, но в этом случае у вас будут более серьезные проблемы, чем удержание вашего пользователя внутри приложения. Тем не менее, даже если Android каким-то образом инициирует сигнал об уничтожении нашего приложения, всякий раз, когда вы пытаетесь вернуться домой, ОС будет пытаться снова запустить наше приложение, поскольку оно является средством запуска по умолчанию, таким образом удерживая пользователя заблокированным.
Создание задней двери
В качестве быстрого объяснения, в настройках приложения было место, где вы могли ввести PIN-код, чтобы разблокировать приложение. Если PIN-код правильный, он отключит ограничения, установленные нашими методами onPause и onBackPressed, выполнив простой условный оператор. Оттуда пользователю будет разрешено входить в настройки через меню быстрого переключения. После этого вы всегда можете установить стандартную программу запуска по умолчанию, и это полностью избавит вас от приложения. Есть много способов справиться с этой частью, но хорошо иметь механизм для отключения тех же ограничений, которые вы установили. Может быть, вы могли бы сделать аутентификацию по отпечатку пальца, чтобы разблокировать. Возможности почти безграничны.
Подведение итогов
В конце концов, у меня осталось приложение, которое никто не мог закрыть или убить. Даже перезагрузка устройства не помогла бы, так как оно снова включилось бы непосредственно в программу запуска по умолчанию, которая в настоящее время является нашим приложением. Это оказалось полезным для нашего проекта, и удовлетворение от попытки попробовать что-то настолько дурацкое и нестандартное было действительно большим и очень мотивирующим.
Существует множество устройств и вариантов использования, в которых Android упростил жизнь разработчикам. В наши дни написать приложение для Android намного проще, чем использовать множество различных языков и инструментов для конкретных платформ. Подумайте об устройствах IoT, приложениях для киосков, системах точек продаж, навигационных и платежных шлюзах для такси и многом другом.
Это случаи использования, когда Android упростил разработку приложений, а также нишевые случаи использования, когда вы хотели бы ограничить доступ аналогично тому, что мы продемонстрировали в этой статье.