Crearea unei aplicații Android POS care nu poate fi închisă
Publicat: 2022-03-11Lumea dezvoltării aplicațiilor mobile este vastă și în continuă evoluție, cu noi cadre și tehnologii care apar aproape zilnic. Când te gândești la un dispozitiv mobil, probabil că te gândești la telefon sau poate la tabletă, deși nu sunt nicăieri la fel de populare ca smartphone-urile.
iOS de la Apple și Android de la Google domină piața de telefonie mobilă, fiecare având suișuri și coborâșuri în ultimul deceniu. Astăzi, voi vorbi mai multe despre Android și despre utilizarea lui pe dispozitive care nu sunt neapărat mobile.
A fi open-source a avut un efect secundar cu adevărat interesant asupra sistemului de operare mobil al Google. Sigur, ne putem gândi la toate dispozitivele Android diferite de la diverse companii de smartphone-uri, dar cum rămâne cu toate dispozitivele care rulează Android care nu sunt mobile? Totul, de la frigidere, cuptoare inteligente, încuietori uși sau chiar dispozitive de la punctul de vânzare (POS) poate rula Android în zilele noastre. Acestea din urmă sunt motivul pentru care am ajuns să scriu acest articol.
Sisteme POS Android
În urmă cu aproximativ un an, am ajuns să mă joc cu un dispozitiv Android care nu era nimic obișnuit și nu este ceva ce majoritatea oamenilor ar putea folosi. Dispozitivul în cauză este un sistem POS bazat pe Android de la un furnizor chinez care are și o imprimantă termică integrată (cum ar fi cele folosite pentru a tipări bonurile în magazine sau la bancomate).
Cea mai mare surpriză, totuși, a fost software-ul său: rula o versiune de Android OS. Dacă îmi amintesc corect, la momentul respectiv rula Android 8 sau Android Oreo dacă preferați numele de cod Google. Dispozitivul în sine arată ca un dispozitiv POS portabil de școală veche, dar în loc de tastatura fizică în care ați introduce PIN-ul, are un ecran tactil capacitiv precum cele folosite în telefoanele Android de altădată.
Cerința mea era ușoară: trebuia să văd dacă există o modalitate prin care putem folosi caracteristicile acestui dispozitiv, cum ar fi imprimanta termică, rulând și aplicația pe care o dezvoltam. De îndată ce mi-am dat seama că cerința în sine este posibilă, mi-a venit în atenție o altă problemă: securitatea .
Chestia este că, dacă aveți un dispozitiv care se ocupă de plăți cu cardul și alte tipuri de tranzacții, este posibil să nu doriți ca același dispozitiv să poată rula TikTok, Gmail sau Snapchat. Acest dispozitiv se comporta exact ca o tabletă și chiar a venit cu Google Play Store preinstalat. Imaginați-vă că mergeți la un mic magazin și vă vedeți casierul făcând selfie-uri, deschizând e-mailuri de la un prinț nigerian și răsfoind site-uri web ciudate, pline de malware.
Și apoi, casieria vă înmânează același dispozitiv pentru a vă introduce codul PIN. Personal, nu m-aș simți în siguranță dacă furnizez informațiile cardului meu de credit pe un astfel de dispozitiv.
Blocarea utilizatorilor din meniurile Android
Lăsând deoparte securitatea, a trebuit să accept o provocare și mai importantă: a trebuit să blochez persoana folosind dispozitivul Android POS în aplicația mea. Jocul cu sistemul de operare nu a fost o opțiune, deoarece aceste dispozitive au fost livrate persoanelor netehnice.
Sigur, casierii sunt mai mult decât capabili să instaleze o aplicație, dar majoritatea dintre ei nu au putut flash ROM-uri personalizate sau să se ocupe de alte operațiuni de nivel inferior. Aplicația în sine a fost scrisă în React Native, deși acest lucru este irelevant în acest context. Toate modificările pe care le-am făcut sunt în cod nativ Java, așa că indiferent de ce folosiți pentru a vă dezvolta aplicația principală, aceste modificări ar trebui să funcționeze.
Ca o mică declinare a răspunderii, această procedură funcționează numai pentru aplicațiile Android . Apple nu ne oferă controlul de care avem nevoie pentru a realiza cu ușurință așa ceva pe un iPhone sau iPad, ceea ce este de înțeles având în vedere natura închisă a iOS.
Există patru moduri în care un utilizator ar putea părăsi o aplicație:
- Folosiți butonul Acasă .
- Folosiți butonul Înapoi .
- Folosiți butonul Recente .
- Părăsiți aplicația prin bara de notificări .
Fie făcând clic pe o notificare recentă, fie accesând setările din acea bară, ar determina un utilizator să părăsească aplicația noastră. Aveți și gesturi, dar la sfârșitul zilei, aceste gesturi declanșează exact aceleași acțiuni ca și apăsările obișnuite de buton.
De asemenea, a avea un sistem PIN pentru a debloca aplicația poate fi cu adevărat util pentru ca cineva să gestioneze dispozitivul. În acest fel, doar cineva care deține un PIN ar putea instala o versiune diferită a aplicației, fără a oferi acces mai profund utilizatorului final.
Butonul Acasă
Pentru a împiedica un utilizator să apese butonul Acasă, nu trebuie să-l dezactivăm efectiv.
O caracteristică utilă a Androidului este disponibilitatea diferitelor lansatoare. De obicei, aceste aplicații vă oferă diferite ecrane de pornire, sertare de aplicații și acces la diverse personalizări ale UI. Fiecare dispozitiv Android are unul preinstalat de producător. În cele din urmă, acestea sunt doar aplicații normale, obișnuite, cu o excepție mică, dar crucială.
Asta înseamnă că, dacă sistemul de operare ar putea recunoaște aplicația noastră ca fiind un lansator, am putea-o seta ca lansator implicit. Efectul secundar al acestui lucru este că de fiecare dată când apăsați butonul Acasă, dispozitivul vă va duce la lansatorul Acasă. Și dacă aplicația noastră este lansatorul Home, atunci practic, acest buton Home devine inutil. Pentru a face asta, trebuie să edităm fișierul XML AndroidManifest în proiectul nostru Android și să adăugăm aceste două linii de cod:
<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>
Prima linie va face ca aplicația noastră să fie eligibilă pentru a fi selectată în cazul în care utilizatorul apasă butonul Acasă, iar a doua linie permite ca aplicația noastră să fie setată ca implicită ori de câte ori are loc această acțiune.
Acum, singurul lucru rămas de făcut a fost ca agentul de teren să instaleze aplicația pe dispozitiv atunci când o livrează clientului. Ori de câte ori instalați o aplicație care are potențialul de a fi un lansator, Android vă va întreba dacă doriți să utilizați un alt lansator și dacă doriți să îl setați ca implicit.
Așa că acum, dacă ai apăsat butonul Acasă sau dacă ai șters toate aplicațiile recente, dispozitivul te-ar direcționa automat către aplicația mea.
Butonul Înapoi
În continuare, trebuie să ne ocupăm de butonul Înapoi. Aplicațiile mobile oferă de obicei o modalitate pe ecran de a trece înapoi prin ecrane, mai ales că multe dispozitive nu au o tastă dedicată „înapoi”.
În urmă cu câțiva ani, acesta a fost cazul dispozitivelor Apple iOS, care prezentau designul lor deja iconic cu un singur buton fizic sub ecran. Cu toate acestea, în ultimii ani, majoritatea dispozitivelor Android au renunțat și la butoanele fizice Acasă. Mai întâi, s-au mutat la butoanele de pe ecran, iar acum vedem că sunt eliminate treptat de telefoane în favoarea gesturilor, pe măsură ce producătorii de telefoane trec la dispozitive pe tot ecranul, cu rame și bărbie mici.
Acest lucru înseamnă că butonul Înapoi pe care îl oferă Android în mod implicit nu este cu adevărat necesar, iar pentru a face acest buton complet inutil, trebuie doar să adăugăm un simplu bloc de cod în activitatea noastră:
@Override public void onBackPressed() { }

Acesta este un bloc de cod destul de simplu: activitatea noastră principală ne permite să interceptăm ori de câte ori un utilizator apasă butonul Înapoi. În cazul nostru, deoarece nu dorim ca utilizatorul să apese acel buton de prea multe ori pentru a ieși din aplicație, putem pur și simplu să suprascriem metoda implicită cu una care nu face nimic, spunând aplicației noastre să nu facă nimic în cazul în care Înapoi butonul este apăsat.
Acesta este modul în care unele aplicații solicită confirmarea înainte de a le ieși accidental, revenind de prea multe ori înapoi.
Butonul Recente
Încă trebuie să ne ocupăm de butonul Recente, iar acesta este cel mai complicat. De asemenea, aceasta nu este cu siguranță o bună practică sau ceva pe care ar trebui să-l trimiteți în Magazinul Play, dar funcționează pentru cazul nostru de nișă aici.
În același mod în care activitatea principală ne permite să știm când este apăsat butonul Înapoi, ne permite, de asemenea, să știm când aplicația este întreruptă. Ce inseamna asta? Acest cod este declanșat de fiecare dată când aplicația noastră trece de la a fi aplicația în prim-plan la a fi în fundal.
Când interceptăm acest eveniment, vom obține ID-ul sarcinii aplicației noastre curente și vom spune managerului de activitate să mute această sarcină în față. Pentru a face acest lucru, avem nevoie de o permisiune specială în același fișier manifest Android pe care l-am editat mai devreme.
<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>
Acest lucru ne va permite să citim sarcinile în curs și să facem modificări la aceste sarcini. De asemenea, mai trebuie să interceptăm momentul în care aplicația noastră este trimisă în fundal. Putem anula din nou metoda onPause
în activitatea noastră.
Aici, obținem managerul de activități și îl forțăm să mute o anumită sarcină în prim-plan. În cazul nostru, sarcina respectivă este cea care tocmai a fost trimisă în fundal (aplicația noastră).
@Override public void onPause() { super.onPause(); ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); activityManager.moveTaskToFront(getTaskId(), 0); }
Acum, de fiecare dată când doriți să accesați meniul de recente, aplicația se va reorienta automat. Sigur, s-ar putea ca ecranul să pâlpâie uneori, dar nu veți putea ieși din această aplicație. Și iată încă un lucru tare - îți amintești cum am spus că poți ieși și făcând clic pe o notificare sau mergând direct la setări prin bara de notificări? Ei bine, efectuarea acelor acțiuni va pune aplicația în fundal, ceea ce va declanșa codul nostru, iar apoi utilizatorul este împins imediat înapoi.
Toate acestea se întâmplă atât de repede încât utilizatorul nu va observa ce se întâmplă în fundal. De asemenea, o altă parte frumoasă a acestei abordări este că comutatoarele rapide sunt încă disponibile. Puteți în continuare să selectați o rețea wifi, de exemplu, sau să dezactivați sunetele, dar orice vă impune să accesați aplicația Setări actuală nu este permis.
Soluția
Nu sunt sigur că acesta este cel mai bun mod de a face asta, dar a fost totuși un proces cu adevărat interesant în timp ce cercetam un subiect despre care nici măcar nu știam că este posibil. Și funcționează! Un cuvânt de avertizare: în acest moment, există doar două moduri prin care puteți părăsi aplicația ca dezvoltator – fie reinstalați sistemul de operare, fie opriți/dezinstalați aplicația prin ADB.
Dacă pierzi cumva conexiunea ADB la dispozitiv, nu știu o modalitate ușoară care te-ar putea scoate. Pentru a evita asta, am ajuns să construiesc un sistem PIN.
Huse Edge
Există câteva situații de care trebuie să ne asigurăm că le luăm în considerare. În primul rând, ce se întâmplă dacă un dispozitiv se repornește? Nu trebuie să fie o repornire manuală, poate fi și o blocare a sistemului de operare.
Deoarece am setat aplicația noastră ca lansator implicit mai devreme, de îndată ce sistemul de operare pornește o copie de rezervă, aceasta ar trebui să pornească automat aplicația noastră. Dar de unde știe Android să încarce ecranul de pornire la pornire? Se datorează faptului că, practic, încarcă lansatorul implicit. Deoarece suntem lansatorul implicit în acest moment, repornirile nu ar trebui să fie o problemă. Și ar putea Android să distrugă aplicația noastră la un moment dat? Teoretic, ar putea ucide aplicația dacă memoria RAM se umple, dar în viața reală, acest lucru este aproape imposibil. Deoarece aplicația noastră nu poate fi închisă, nimeni nu poate deschide alte aplicații și astfel memoria RAM nu ar trebui să se umple.
Singurul mod în care mă gândesc că se umple este dacă aplicația noastră are o scurgere uriașă de memorie, dar în acest caz, ați avea probleme mai mari decât păstrarea utilizatorului în interiorul aplicației. Totuși, chiar dacă Android declanșează cumva un semnal de ucidere pentru aplicația noastră, de fiecare dată când încercați să vă întoarceți acasă, sistemul de operare va încerca să pornească din nou aplicația noastră, deoarece este lansatorul implicit, păstrând astfel utilizatorul blocat.
Construirea unei uși din spate
Ca o explicație rapidă, a existat un loc în setările aplicației unde puteai introduce un PIN pentru a debloca aplicația. Dacă PIN-ul este corect, acesta va dezactiva limitările stabilite de metodele noastre onPause și onBackPressed printr-o declarație condiționată simplă. De acolo, unui utilizator i se va permite să introducă setările prin meniul de comutare rapidă. După aceea, puteți seta oricând lansatorul implicit înapoi la cel de stoc, iar asta vă va scoate complet din aplicație. Există o mulțime de moduri în care se poate gestiona această parte, dar este bine să ai un mecanism care să dezactiveze aceleași limitări pe care le-ai aplicat. Poate ați putea face o autentificare cu amprentă pentru a debloca. Posibilitățile sunt aproape nesfârșite.
Încheierea
În cele din urmă, am rămas cu o aplicație pe care nimeni nu o putea închide sau ucide. Nici măcar repornirea dispozitivului nu ar ajuta, deoarece s-ar reporni direct la lansatorul implicit, care este în prezent aplicația noastră. S-a dovedit util pentru proiectul nostru, iar satisfacția de a încerca ceva atât de ciudat și de neconform a fost într-adevăr grozavă și foarte motivantă.
Există o mulțime de dispozitive și cazuri de utilizare în care Android a făcut viața mai ușoară dezvoltatorilor. În zilele noastre, scrierea unei aplicații Android este mult mai ușoară decât utilizarea multor limbaje și instrumente specifice platformei. Gândiți-vă la dispozitive IoT, aplicații de chioșc, sisteme de puncte de vânzare, gateway-uri de navigare și plată pentru taxiuri și multe altele.
Acestea sunt cazuri de utilizare în care Android a ușurat dezvoltarea aplicațiilor, dar și cazuri de utilizare de nișă în care ați dori să restricționați accesul într-un mod similar cu ceea ce am demonstrat în acest articol.