Costruire un'app POS Android che non può essere chiusa

Pubblicato: 2022-03-11

Il mondo dello sviluppo di app mobili è vasto e in continua evoluzione, con nuovi framework e tecnologie che spuntano quasi quotidianamente. Quando pensi a un dispositivo mobile, probabilmente pensi al tuo telefono o forse al tuo tablet, anche se non sono così popolari come gli smartphone.

iOS di Apple e Android di Google dominano il mercato mobile, ciascuno con alti e bassi negli ultimi dieci anni. Oggi parlerò di più di Android e del suo utilizzo su dispositivi che non sono necessariamente mobili.

L'essere open source ha avuto un effetto collaterale davvero interessante sul sistema operativo mobile di Google. Certo, potremmo pensare a tutti i diversi fork Android di varie aziende di smartphone, ma che dire di tutti i dispositivi con Android che non sono mobili? Tutto ciò che va da frigoriferi, forni intelligenti, serrature o persino dispositivi POS (Point of Sale) può eseguire Android al giorno d'oggi. Questi ultimi sono il motivo per cui ho finito per scrivere questo articolo.

Al giorno d'oggi, i dispositivi POS (Point of Sale) possono eseguire Android

Sistemi POS Android

Circa un anno fa, ho avuto modo di giocare con un dispositivo Android che era tutt'altro che ordinario e non è qualcosa che la maggior parte delle persone probabilmente utilizzerà. Il dispositivo in questione è un sistema POS basato su Android di un fornitore cinese che dispone anche di una stampante termica integrata (come quelle utilizzate per stampare le ricevute nei negozi o sugli sportelli automatici).

La sorpresa più grande, tuttavia, è stata il suo software: stava eseguendo una versione stock di Android di Android. Se ricordo bene, all'epoca era in esecuzione Android 8 o Android Oreo se preferisci i nomi in codice di Google. Il dispositivo stesso sembra un dispositivo POS portatile della vecchia scuola, ma invece della tastiera fisica in cui inseriresti il ​​PIN, sfoggia un touchscreen capacitivo come quelli utilizzati nei telefoni Android di una volta.

La mia richiesta era semplice: dovevo vedere se c'era un modo in cui avremmo potuto utilizzare le funzionalità di questo dispositivo come la stampante termica mentre eseguivamo anche l'app che stavamo sviluppando. Non appena ho capito che il requisito in sé era possibile, mi è venuto in mente un altro problema: la sicurezza .

Il fatto è che se hai un dispositivo che gestisce pagamenti con carta e altri tipi di transazioni, potresti non volere che lo stesso dispositivo sia in grado di eseguire TikTok, Gmail o Snapchat. Questo dispositivo si comportava esattamente come un tablet e arrivava persino con il Play Store di Google preinstallato. Immagina di andare in un piccolo minimarket e di vedere il tuo cassiere fare selfie, aprire e-mail da un principe nigeriano e navigare in strani siti Web pieni di malware.

E successivamente, il cassiere ti consegna lo stesso dispositivo per inserire il tuo PIN. Personalmente, non mi sentirei al sicuro nel fornire i dati della mia carta di credito su un dispositivo del genere.

Blocco degli utenti dai menu di Android

Sicurezza a parte, ho dovuto affrontare una sfida ancora più importante: ho dovuto bloccare la persona utilizzando il dispositivo POS Android all'interno della mia app. Giocare con il sistema operativo non era un'opzione poiché questi dispositivi venivano consegnati a persone non tecniche.

Certo, i cassieri sono più che in grado di installare un'applicazione, ma la maggior parte di loro non è in grado di eseguire il flashing di ROM personalizzate o gestire altre operazioni di livello inferiore. L'app stessa è stata scritta in React Native, sebbene ciò sia irrilevante in questo contesto. Tutte le modifiche che ho apportato sono nel codice Java nativo, quindi non importa cosa stai usando per sviluppare la tua applicazione principale, queste modifiche dovrebbero funzionare.

Come piccolo disclaimer, questa procedura funziona solo per le app Android . Apple non ci dà il controllo di cui abbiamo bisogno per realizzare facilmente qualcosa del genere su un iPhone o iPad, il che è comprensibile data la natura chiusa di iOS.

Esistono quattro modi in cui un utente potrebbe uscire da un'applicazione:

  • Usa il pulsante Home .
  • Usa il pulsante Indietro .
  • Usa il pulsante Recenti .
  • Abbandona la tua app tramite la barra delle notifiche .

Se si fa clic su una notifica recente o si accede alle impostazioni da quella barra, l'utente esce dalla nostra applicazione. Hai anche dei gesti, ma alla fine della giornata, questi gesti attivano le stesse identiche azioni delle normali pressioni dei pulsanti.

Inoltre, avere un sistema PIN per sbloccare l'applicazione può essere davvero utile per qualcuno che gestisca il dispositivo. In questo modo, solo chi possiede un PIN potrebbe installare una versione diversa dell'applicazione, senza offrire un accesso più approfondito all'utente finale.

Il pulsante Home

Per impedire a un utente di premere il pulsante Home, non è necessario disabilitarlo effettivamente.

Una caratteristica utile di Android è la disponibilità di diversi lanciatori. Di solito, queste app offrono diverse schermate iniziali, cassetti delle app e accesso a varie personalizzazioni dell'interfaccia utente. Ogni dispositivo Android ne ha uno preinstallato dal produttore. In definitiva, queste sono solo app normali e regolari con una piccola ma cruciale eccezione.

Ciò significa che se il sistema operativo potesse riconoscere la nostra applicazione come launcher, potremmo impostarla come launcher predefinito. L'effetto collaterale di questo è che ogni volta che premi il pulsante Home, il dispositivo ti porterà al launcher Home. E se la nostra applicazione è il launcher Home, in pratica questo pulsante Home diventa inutile. Per farlo, dobbiamo modificare il file XML AndroidManifest nel nostro progetto Android e aggiungere queste due righe di codice:

 <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 prima riga renderà la nostra app idonea a essere selezionata nel caso in cui l'utente prema il pulsante Home e la seconda riga consentirà alla nostra app di essere impostata come predefinita ogni volta che si verifica questa azione.

Ora, l'unica cosa rimasta da fare era che l'agente sul campo installasse l'applicazione sul dispositivo quando la consegnava al client. Ogni volta che installi un'app che ha il potenziale per essere un lanciatore, Android ti chiederà se desideri utilizzare un altro lanciatore e se desideri impostarlo come predefinito.

Quindi ora, se hai premuto il pulsante Home o se hai cancellato tutte le tue applicazioni recenti, il dispositivo ti indirizzerà automaticamente alla mia applicazione.

Costruire un'app POS Android che non può essere chiusa

Il pulsante Indietro

Successivamente, dobbiamo gestire il pulsante Indietro. Le app mobili di solito forniscono un modo su schermo per tornare indietro tra le schermate, soprattutto perché molti dispositivi non dispongono di un tasto "indietro" dedicato.

Alcuni anni fa, questo era il caso dei dispositivi iOS di Apple, che presentavano il loro già iconico design con un solo pulsante fisico sotto lo schermo. Tuttavia, negli ultimi anni, anche la maggior parte dei dispositivi Android ha abbandonato i pulsanti Home fisici. In primo luogo, sono passati ai pulsanti sullo schermo e ora li stiamo vedendo eliminare gradualmente i telefoni a favore dei gesti, poiché i produttori di telefoni si spostano su dispositivi a tutto schermo con cornici e mento piccoli.

Ciò significa che il pulsante Indietro che Android fornisce per impostazione predefinita non è realmente necessario e per rendere questo pulsante completamente inutile, dobbiamo solo aggiungere un semplice blocco di codice nella nostra attività:

 @Override public void onBackPressed() { } 

Costruire un'app POS Android che non può essere chiusa

Questo è un blocco di codice piuttosto semplice: la nostra attività principale ci consente di intercettare ogni volta che un utente preme il pulsante Indietro. Nel nostro caso, poiché non vogliamo che l'utente prema quel pulsante troppe volte per uscire dall'app, possiamo semplicemente sovrascrivere il metodo predefinito con uno che non fa nulla, dicendo alla nostra app di non fare nulla nel caso in cui il Indietro viene premuto il pulsante.

È così che alcune applicazioni chiedono conferma prima di uscire accidentalmente da esse tornando indietro troppe volte.

Il pulsante Recenti

Dobbiamo ancora gestire il pulsante Recenti, e questo è il più complicato. Inoltre, questa non è sicuramente una best practice o qualcosa che dovresti inviare al Play Store, ma funziona per il nostro caso di nicchia qui.

Allo stesso modo in cui l'attività principale ci consente di sapere quando viene premuto il pulsante Indietro, ci consente anche di sapere quando l'app è in pausa. Cosa significa questo? Questo codice viene attivato ogni volta che la nostra app passa dall'essere l'applicazione in primo piano a quella in background.

Quando intercettiamo questo evento, otterremo l'ID attività della nostra attuale applicazione e diremo al gestore attività di spostare questa attività in primo piano. Per fare ciò, abbiamo bisogno di un'autorizzazione speciale nello stesso file manifest Android che abbiamo modificato in precedenza.

 <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>

Questo ci consentirà di leggere le attività in corso e apportare modifiche a queste attività. Inoltre, dobbiamo ancora intercettare il momento in cui la nostra applicazione viene inviata in background. Possiamo nuovamente sovrascrivere il metodo onPause nella nostra attività.

Qui, otteniamo il task manager e lo forziamo a spostare un'attività specifica in primo piano. Nel nostro caso, quel compito specifico è quello che è appena stato inviato in background (la nostra applicazione).

 @Override public void onPause() { super.onPause(); ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); activityManager.moveTaskToFront(getTaskId(), 0); }

Ora, ogni volta che vuoi andare al menu Recenti, l'applicazione si rifocalizzerà automaticamente. Certo, a volte potresti avere un piccolo sfarfallio dello schermo, ma non sarai in grado di uscire da questa applicazione. Ed ecco un'altra cosa interessante: ricordi come ho detto che potresti anche uscire facendo clic su una notifica o andando direttamente alle impostazioni tramite la barra delle notifiche? Bene, l'esecuzione di queste azioni metterà l'app in background, che attiverà il nostro codice, e quindi l'utente verrà respinto.

Tutto ciò accade così rapidamente che l'utente non si accorgerà di ciò che accade in background. Inoltre, un'altra parte interessante di questo approccio è che i tuoi commutatori rapidi sono ancora disponibili. Puoi comunque selezionare una rete wifi, ad esempio, o disabilitare i suoni, ma tutto ciò che richiede l'accesso all'app Impostazioni effettiva non è consentito.

Costruire un'app POS Android che non può essere chiusa

La soluzione

Non sono sicuro che questo sia il modo migliore per farlo, ma è stato comunque un processo davvero interessante durante la ricerca su un argomento che non sapevo nemmeno fosse possibile. E funziona! Un avvertimento: a questo punto, ci sono solo due modi per uscire dall'applicazione come sviluppatore: reinstallare il sistema operativo o uccidere/disinstallare l'app tramite ADB.

Se in qualche modo perdi la connessione ADB al dispositivo, non sono a conoscenza di un modo semplice che potrebbe farti uscire. Per evitarlo, ho finito per creare un sistema PIN.

Casi limite

Ci sono alcune situazioni di cui dobbiamo assicurarci di tenere conto. Prima di tutto, cosa succede se un dispositivo si riavvia? Non deve essere un riavvio manuale, può anche essere un arresto anomalo del sistema operativo.

Poiché abbiamo impostato la nostra applicazione come programma di avvio predefinito in precedenza, non appena il sistema operativo si riavvia, dovrebbe avviare automaticamente la nostra applicazione. Ma come fa Android a caricare la schermata iniziale all'avvio? È perché fondamentalmente carica solo il tuo launcher predefinito. Poiché a questo punto siamo il programma di avvio predefinito, i riavvii non dovrebbero essere un problema. E Android potrebbe uccidere la nostra applicazione a un certo punto? In teoria, potrebbe uccidere l'app se la memoria RAM si riempie, ma nella vita reale questo è quasi impossibile. Poiché la nostra app non è chiudibile, nessuno può aprire altre app, quindi la memoria RAM non dovrebbe riempirsi.

L'unico modo in cui posso pensare che si riempia è se la nostra applicazione ha un'enorme perdita di memoria, ma in tal caso, avresti problemi più grandi che mantenere il tuo utente all'interno dell'applicazione. Tuttavia, anche se Android in qualche modo attiva un segnale di kill alla nostra applicazione, ogni volta che provi a tornare a casa, il sistema operativo tenterà di riavviare la nostra app poiché è il launcher predefinito, mantenendo così l'utente bloccato.

Costruire una porta sul retro

Come rapida spiegazione, c'era un punto nelle impostazioni dell'applicazione in cui era possibile inserire un PIN per sbloccare l'app. Se il PIN è corretto, disabiliterà le limitazioni impostate dai nostri metodi onPause e onBackPressed eseguendo una semplice istruzione condizionale. Da lì, un utente potrebbe accedere alle impostazioni tramite il menu di commutazione rapida. Successivamente, puoi sempre reimpostare il programma di avvio predefinito su quello di serie e questo ti farebbe uscire completamente dall'app. Ci sono molti modi in cui si potrebbe gestire questa parte, ma è bene avere un meccanismo per disabilitare le stesse limitazioni che hai messo in atto. Forse potresti eseguire un'autenticazione tramite impronta digitale per sbloccare. Le possibilità sono quasi infinite.

Costruire un'app POS Android che non può essere chiusa

Avvolgendo

Alla fine, mi è rimasta un'applicazione che nessuno poteva chiudere o uccidere. Anche il riavvio del dispositivo non sarebbe di aiuto poiché si riaccenderebbe direttamente al programma di avvio predefinito, che attualmente è la nostra applicazione. Si è rivelato utile per il nostro progetto e la soddisfazione di provare qualcosa di così bizzarro e fuori luogo è stata davvero grande e altamente motivante.

Esistono molti dispositivi e casi d'uso in cui Android ha semplificato la vita degli sviluppatori. Al giorno d'oggi, scrivere un'applicazione Android è molto più semplice che utilizzare molti linguaggi e strumenti specifici per piattaforma diversi. Pensa ai dispositivi IoT, alle applicazioni per chioschi, ai sistemi di punti vendita, ai gateway di navigazione e di pagamento per i taxi e molti altri.

Questi sono casi d'uso in cui Android ha semplificato lo sviluppo di app, ma anche casi d'uso di nicchia in cui si desidera limitare l'accesso in modo simile a quanto illustrato in questo articolo.