Créer une application Android POS qui ne peut pas être fermée
Publié: 2022-03-11Le monde du développement d'applications mobiles est vaste et en constante évolution, avec de nouveaux frameworks et technologies qui apparaissent presque quotidiennement. Lorsque vous pensez à un appareil mobile, vous pensez probablement à votre téléphone ou peut-être à votre tablette, même s'ils sont loin d'être aussi populaires que les smartphones.
iOS d'Apple et Android de Google dominent le marché mobile, chacun ayant connu des hauts et des bas au cours de la dernière décennie. Aujourd'hui, je vais parler davantage d'Android et de son utilisation sur des appareils qui ne sont pas forcément mobiles.
Être open-source a eu un effet secondaire très intéressant sur le système d'exploitation mobile de Google. Bien sûr, nous pouvons penser à toutes les différentes fourches Android de diverses sociétés de smartphones, mais qu'en est-il de tous les appareils fonctionnant sous Android qui ne sont pas mobiles ? De nos jours, tout, des réfrigérateurs aux fours intelligents, en passant par les serrures de porte ou même les appareils de point de vente (POS), peut fonctionner sous Android. Ces derniers sont la raison pour laquelle j'ai fini par écrire cet article.
Systèmes de point de vente Android
Il y a environ un an, j'ai pu jouer avec un appareil Android qui était tout sauf ordinaire, et ce n'est pas quelque chose que la plupart des gens sont susceptibles d'utiliser. L'appareil en question est un système de point de vente basé sur Android d'un fournisseur chinois qui dispose également d'une imprimante thermique intégrée (comme celles utilisées pour imprimer les reçus dans les magasins ou sur les guichets automatiques).
La plus grande surprise, cependant, était son logiciel : il exécutait une version osseuse d'Android. Si je me souviens bien, à l'époque, il fonctionnait sous Android 8, ou Android Oreo si vous préférez les noms de code Google. L'appareil lui-même ressemble à un appareil de point de vente portable de la vieille école, mais au lieu du clavier physique sur lequel vous entreriez votre code PIN, il arbore un écran tactile capacitif comme ceux utilisés dans les téléphones Android d'antan.
Mon exigence était simple : je devais voir s'il y avait un moyen d'utiliser les fonctionnalités de cet appareil telles que l'imprimante thermique tout en exécutant l'application que nous développions. Dès que j'ai réalisé que l'exigence elle-même était possible, un autre problème a attiré mon attention : la sécurité .
Le fait est que si vous avez un appareil qui gère les paiements par carte et d'autres types de transactions, vous ne voudrez peut-être pas que ce même appareil puisse exécuter TikTok, Gmail ou Snapchat. Cet appareil se comportait exactement comme une tablette, et il était même livré avec le Play Store de Google préinstallé. Imaginez que vous allez dans un petit dépanneur et voyez votre caissier prendre des selfies, ouvrir des e-mails d'un prince nigérian et parcourir des sites Web étranges et infestés de logiciels malveillants.
Et ensuite, le caissier vous tend le même appareil pour entrer votre code PIN. Personnellement, je ne me sentirais pas en sécurité en fournissant mes informations de carte de crédit sur un tel appareil.
Verrouillage des utilisateurs hors des menus Android
Sécurité mise à part, j'ai dû relever un défi encore plus important : je devais verrouiller la personne utilisant l'appareil Android POS dans mon application. Jouer avec le système d'exploitation n'était pas une option puisque ces appareils étaient livrés à des personnes non techniques.
Bien sûr, les caissiers sont plus que capables d'installer une application, mais la plupart d'entre eux ne peuvent pas flasher de ROM personnalisées ou gérer d'autres opérations de niveau inférieur. L'application elle-même a été écrite en React Native, bien que cela ne soit pas pertinent dans ce contexte. Toutes les modifications que j'ai apportées sont en code Java natif, donc peu importe ce que vous utilisez pour développer votre application principale, ces ajustements devraient fonctionner.
En guise de petit avertissement, cette procédure ne fonctionne que pour les applications Android . Apple ne nous donne pas le contrôle dont nous avons besoin pour accomplir facilement quelque chose comme ça sur un iPhone ou un iPad, ce qui est compréhensible compte tenu de la nature fermée d'iOS.
Un utilisateur peut éventuellement quitter une application de quatre manières :
- Utilisez le bouton Accueil .
- Utilisez le bouton Retour .
- Utilisez le bouton Récents .
- Quittez votre application via la barre de notification .
En cliquant sur une notification récente ou en accédant aux paramètres de cette barre, l'utilisateur quitterait notre application. Vous avez également des gestes, mais à la fin de la journée, ces gestes déclenchent exactement les mêmes actions que les pressions régulières sur les boutons.
De plus, avoir un système de code PIN pour déverrouiller l'application peut être très utile pour quelqu'un qui gère l'appareil. De cette façon, seule une personne détenant un code PIN serait en mesure d'installer une version différente de l'application, sans offrir un accès plus approfondi à l'utilisateur final.
Le bouton d'accueil
Afin d'empêcher un utilisateur d'appuyer sur le bouton Accueil, nous n'avons pas besoin de le désactiver.
Une fonctionnalité utile d'Android est la disponibilité de différents lanceurs. Habituellement, ces applications vous offrent différents écrans d'accueil, tiroirs d'applications et accès à diverses personnalisations de l'interface utilisateur. Chaque appareil Android en a un préinstallé par le fabricant. En fin de compte, ce ne sont que des applications normales et régulières avec une petite mais cruciale exception.
Cela signifie que si le système d'exploitation pouvait reconnaître notre application comme étant un lanceur, nous pourrions la définir comme lanceur par défaut. L'effet secondaire de ceci est que chaque fois que vous appuyez sur le bouton Accueil, l'appareil vous amène au lanceur d'accueil. Et si notre application est le lanceur d'accueil, alors fondamentalement, ce bouton d'accueil devient inutile. Pour ce faire, nous devons éditer le fichier XML AndroidManifest dans notre projet Android et ajouter ces deux lignes de code :
<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>
La première ligne rendra notre application éligible pour être sélectionnée au cas où l'utilisateur appuie sur le bouton Accueil, et la deuxième ligne permet à notre application d'être définie par défaut chaque fois que cette action se produit.
Désormais, il ne restait plus qu'à l'agent de terrain d'installer l'application sur l'appareil lors de sa livraison au client. Chaque fois que vous installez une application qui a le potentiel d'être un lanceur, Android vous demandera si vous souhaitez utiliser un autre lanceur et si vous souhaitez définir celui-ci par défaut.
Alors maintenant, si vous appuyiez sur le bouton Accueil ou si vous effaciez toutes vos applications récentes, l'appareil vous dirigerait automatiquement vers mon application.
Le bouton Retour
Ensuite, nous devons gérer le bouton Retour. Les applications mobiles offrent généralement un moyen de revenir en arrière à l'écran, d'autant plus que de nombreux appareils n'ont pas de touche « retour » dédiée.
Il y a quelques années, c'était le cas des appareils iOS d'Apple, qui présentaient leur design déjà emblématique avec un seul bouton physique sous l'écran. Cependant, ces dernières années, la plupart des appareils Android ont également abandonné les boutons d'accueil physiques. Tout d'abord, ils sont passés aux boutons à l'écran, et maintenant nous les voyons être progressivement supprimés des téléphones au profit des gestes, alors que les fabricants de téléphones passent à des appareils tout écran avec de petites lunettes et un menton.
Cela signifie que le bouton Retour fourni par défaut par Android n'est pas vraiment nécessaire, et pour rendre ce bouton complètement inutile, il nous suffit d'ajouter un simple bloc de code dans notre activité :
@Override public void onBackPressed() { }

Il s'agit d'un bloc de code assez simple : notre activité principale nous permet d'intercepter chaque fois qu'un utilisateur appuie sur le bouton Précédent. Dans notre cas, puisque nous ne voulons pas que l'utilisateur appuie trop souvent sur ce bouton pour quitter l'application, nous pouvons simplement écraser la méthode par défaut avec une méthode qui ne fait rien, disant à notre application de ne rien faire au cas où le Back bouton est enfoncé.
C'est ainsi que certaines applications demandent une confirmation avant que vous ne les quittiez accidentellement en y retournant trop de fois.
Le bouton Récents
Nous devons encore gérer le bouton Récents, et c'est le plus délicat. De plus, ce n'est certainement pas une bonne pratique ou quelque chose que vous devriez pousser sur le Play Store, mais cela fonctionne pour notre cas de niche ici.
De la même manière que l'activité principale nous permet de savoir quand le bouton Retour est enfoncé, cela nous permet également de savoir quand l'application est en pause. Qu'est-ce que ça veut dire? Ce code est déclenché chaque fois que notre application passe de l'application de premier plan à l'arrière-plan.
Lors de l'interception de cet événement, nous obtiendrons l'ID de tâche de notre application actuelle et dirons au gestionnaire d'activité de déplacer cette tâche au premier plan. Pour ce faire, nous avons besoin d'une autorisation spéciale dans le même fichier manifeste Android que nous avons modifié précédemment.
<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>
Cela nous permettra de lire les tâches en cours et d'apporter des modifications à ces tâches. De plus, nous devons toujours intercepter le moment où notre application est envoyée en arrière-plan. Nous pouvons à nouveau remplacer la méthode onPause
dans notre activité.
Ici, nous obtenons le gestionnaire de tâches et le forçons à déplacer une tâche spécifique au premier plan. Dans notre cas, cette tâche spécifique est celle qui vient d'être envoyée en arrière-plan (notre application).
@Override public void onPause() { super.onPause(); ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); activityManager.moveTaskToFront(getTaskId(), 0); }
Désormais, à chaque fois que vous voudrez vous rendre dans le menu Récents, l'application se recentrera automatiquement. Bien sûr, vous pourriez parfois avoir un petit scintillement de l'écran, mais vous ne pourrez pas quitter cette application. Et voici une autre chose sympa - rappelez-vous comment j'ai dit que vous pouviez également quitter en cliquant sur une notification ou en accédant directement aux paramètres via la barre de notification ? Eh bien, l'exécution de ces actions mettra l'application en arrière-plan, ce qui déclenchera notre code, puis l'utilisateur sera repoussé.
Tout cela se produit si rapidement que l'utilisateur ne remarquera pas ce qui se passe en arrière-plan. De plus, un autre aspect intéressant de cette approche est que vos bascules rapides sont toujours disponibles. Vous pouvez toujours sélectionner un réseau Wi-Fi, par exemple, ou désactiver les sons, mais tout ce qui vous oblige à accéder à l'application Paramètres n'est pas autorisé.
La solution
Je ne suis pas sûr que ce soit la meilleure façon de le faire, mais c'était néanmoins un processus vraiment intéressant tout en recherchant un sujet dont je ne savais même pas qu'il était possible. Et il fonctionne! Un mot d'avertissement : à ce stade, il n'y a que deux façons de quitter l'application en tant que développeur : soit vous réinstallez le système d'exploitation, soit vous tuez/désinstallez l'application via ADB.
Si vous perdez d'une manière ou d'une autre la connexion ADB à l'appareil, je ne connais pas de moyen simple de vous en sortir. Pour éviter cela, j'ai fini par construire un système PIN.
Cas Edge
Il y a quelques situations dont nous devons nous assurer que nous tenons compte. Tout d'abord, que se passe-t-il si un appareil redémarre ? Il ne doit pas nécessairement s'agir d'un redémarrage manuel, il peut également s'agir d'un plantage du système d'exploitation.
Étant donné que nous avons défini notre application comme lanceur par défaut plus tôt, dès que le système d'exploitation redémarre, il devrait automatiquement démarrer notre application. Mais comment Android sait-il charger votre écran d'accueil au démarrage ? C'est parce qu'il charge simplement votre lanceur par défaut. Puisque nous sommes le lanceur par défaut à ce stade, les redémarrages ne devraient pas être un problème. Et Android pourrait-il tuer notre application à un moment donné ? Théoriquement, cela pourrait tuer l'application si la mémoire RAM se remplit, mais dans la vraie vie, c'est presque impossible. Étant donné que notre application ne peut pas être fermée, personne ne peut ouvrir d'autres applications et la mémoire RAM ne doit donc pas se remplir.
La seule façon dont je peux penser qu'il se remplisse est si notre application a une énorme fuite de mémoire, mais dans ce cas, vous auriez des problèmes plus importants que de garder votre utilisateur à l'intérieur de l'application. Pourtant, même si Android déclenche d'une manière ou d'une autre un signal de mise à mort sur notre application, chaque fois que vous essayez de rentrer chez vous, le système d'exploitation tentera de redémarrer notre application car il s'agit du lanceur par défaut, gardant ainsi l'utilisateur enfermé.
Construire une porte dérobée
Pour une explication rapide, il y avait un endroit dans les paramètres de l'application où vous pouviez entrer un code PIN pour déverrouiller l'application. Si le code PIN est correct, il désactivera les limitations définies par nos méthodes onPause et onBackPressed en faisant une simple instruction conditionnelle. À partir de là, un utilisateur serait autorisé à entrer les paramètres via le menu à bascule rapide. Ensuite, vous pouvez toujours redéfinir le lanceur par défaut sur celui d'origine, ce qui vous sortirait complètement de l'application. Il existe de nombreuses façons de gérer cette partie, mais il est bon d'avoir un mécanisme pour désactiver les mêmes limitations que vous avez mises en place. Peut-être que vous pourriez faire une authentification par empreinte digitale pour déverrouiller. Les possibilités sont presque infinies.
Emballer
Finalement, je me suis retrouvé avec une application que personne ne pouvait fermer ou tuer. Même le redémarrage de l'appareil n'aiderait pas car il se rallumerait directement sur le lanceur par défaut, qui est actuellement notre application. Cela s'est avéré utile pour notre projet, et la satisfaction d'essayer quelque chose d'aussi farfelu et décalé était en effet grande et très motivante.
Il existe de nombreux appareils et cas d'utilisation où Android a facilité la vie des développeurs. De nos jours, écrire une application Android est beaucoup plus facile que d'utiliser de nombreux langages et outils spécifiques à la plate-forme. Pensez aux appareils IoT, aux applications de kiosque, aux systèmes de point de vente, aux passerelles de navigation et de paiement pour les taxis, et bien d'autres.
Ce sont des cas d'utilisation où Android a facilité le développement d'applications, mais aussi des cas d'utilisation de niche où vous voudriez restreindre l'accès d'une manière similaire à ce que nous avons démontré dans cet article.