UNIVERSITATEA POLITEHNICA TIMIȘOARA FACULTATEA DE AUTOMATICĂ ȘI CALCULATOARE AUTOMATICĂ ȘI INFORMATICĂ APLICATĂ Aplicație Android pentru… [304040]

UNIVERSITATEA POLITEHNICA TIMIȘOARA
FACULTATEA DE AUTOMATICĂ ȘI CALCULATOARE
AUTOMATICĂ ȘI INFORMATICĂ APLICATĂ

Aplicație Android pentru administrarea serviciilor de cazare pe cont propriu

PROIECT DE DIPLOMĂ

Profesor coordonator: Autor:
Ș.l. Dr. Ing. [anonimizat]

2019

1. INTRODUCERE 4
1.1. Contextul de realizare 4
1.2. Tema aplicației 4
1.3. Aplicații similare în raport cu aplicația dezvoltată 5
2. FUNDAMENTARE TEORETICĂ 6
2.1. Java 6
2.2. Firebase 6
2.2.1. Platforma Firebase 6
2.2.2. Consola Firebase 7
2.2.3. Autentificare 8
2.2.4. Realtime Database 9
2.2.5. NoSQL 11
2.2.6. Firebase Storage 12
2.3. Google API 14
2.4. GitHub 16
2.5. GitKraken 17
2.6. Android Studio 19
2.6.1. Informații generale 19
2.6.2. Manifest, Gradle 20
2.6.3. Activități și fragmente 20
2.6.4. Emulatorul 21
2.6.5. Debug și Instant Run 22
3. IMPLEMENTAREA APLICAȚIEI 24
3.1. Arhitectura aplicației 24
3.2. Schemă bloc funcțională 24
3.3. Utilizarea limbajului Java 26
3.4. Elemente XML și resurse 27
3.5. Activități Android 29
3.6. Biblioteci utilizate 50
3.7. Bază de date 52
3.8. Testarea aplicației 55
4. UTILIZAREA APLICAȚIEI 56
4.1. Cerințe de sistem 56
4.2. Pornirea aplicației 56
4.3. Autentificare și înregistrare 57
4.4. Adăugare date în profil 58
4.4.1. Adăugare poză 58
4.4.2. Adăugare servicii 58
4.4.3. Ștergere sau ieșire din cont 60
4.5. Căutare și filtrare 60
4.6. Vizualizarea serviciului în detaliu 62
4.7. Achiziționarea unui serviciu 62
4.8. Starea unei rezervări 63
6. CONCLUZII 65
7. BIBLIOGRAFIE 66

1. INTRODUCERE
1.1. [anonimizat]. [anonimizat]. Ritmul de zi cu zi devine tot mai alerg și avem nevoie de un mod de a ține pasul cu el și de a îi face față. [anonimizat]. Una dintre metodele preferate de relaxare o reprezintă călătoria : [anonimizat], o săptămână la munte sau mare sau chiar și mai mult într-o locație exotică.
Conform unui raport al organizației pentru turism din cadrul Națiunilor Unite peste 1300 de milioane de călători au cheltuit peste 1300 de miliarde de dolari doar în 2017, reprezentând 10% din produsul intern brut la nivel global.
/
Fig. 1: Statistici UNWTO pentru turism, 2017[1]

[anonimizat], este de așteptat ca o [anonimizat].
1.2. Tema aplicației
Cum ar fi ca o singură aplicație să fie suficientă pentru toate serviciile necesare unei vacanțe reușite? O [anonimizat]-[anonimizat], toate informațiile la un singur click distanță. Este ca și cum ai avea propriul tău administrator de vacanță și în care ai puterea de a decide amănunțit cum vrei să arate vacanța ta.
Această soluție este dezvoltată în cadrul lucrării, o [anonimizat]. Numele aplicație sugerează faptul că se poate vizita întreaga lume (cuvântul “World” din limba engleză care se poate traduce ca lume sau planetă) prin intermediul unei aplicații (cuvântul “App” fiind prescurtarea de la “Application”).
Personal sunt un fan al călătoriilor și îmi place să pot organiza pe cont propriu și în detaliu vacanțele, însă de multe ori trebuie să consult o mulțime de site-uri pentru a găsi o combinație convenabilă. Așa a venit ideea de a pune serviciile necesare unei vacanțe reușite într-o singură aplicație : cazare, parcare, ghid turistic, administrator de rezervări.
1.3. Aplicații similare în raport cu aplicația dezvoltată
Vom compara aplicația WorldApp cu două aplicații bine-cunoscute : AirBnb și Visit a city. AirBnb a început ca o aplicație care permitea oricărei persoane să găzduiască călători din toată lumea, contra-cost, iar acum are și oferte pentru diferite experiențe, inclusiv ghid turistic, dar și recomandări de restaurante. Visit a city este o aplicație care se ocupă exclusiv de oferte de tururi prin diferite orașe și are milioane de utilizatori.

CaracteristiciWorldAppAirBnb
Visit a cityOpțiuni de cazareDaDaNuOpțiuni de parcare
DaNuNuOpțiuni de tur ghidatDaDaDaRecenziiNuDaDa
Încărcare servicii ca persoană fizicăDaDaDaGalerie fotoDaDaDaHartă interactivăDaDaNu

Filtrare intuitivăDaDa
NuRecomandăriNuDa
DaNecesită autentificareDaDaNu
Tabel 1 : WorldApp în comparație cu AirBnb și Visitacity
După cum observăm în tabelul de mai sus punctul forte al aplicației WorldApp este reprezentat de opțiunea de a închiria un loc de parcare. Acest serviciu reprezintă oportunitatea de intrare pe o piață ultra-competitivă și cu jucători foarte mari în domeniu. Deoarece foarte multe persoane călătoresc sau fac naveta cu mașina locurile de parcare reprezintă o problemă în toate orașele mari. Un eventual succes al serviciului de parcare ar aduce utilizatori care să încerce și celelalte servicii ale aplicației.
2. FUNDAMENTARE TEORETICĂ
2.1. Java
Este un limbaj orientat pe obiecte care face parte din familia C, alături de C, C++ și C#.
Deși este un program cu o vechime considerabilă pentru domeniul IT & C, a rezistat alături de limbajele mai noi și mai dinamice, el fiind de asemenea îmbunătățit constant. Are aplicații în toate subdomeniile industriei, cel mai des fiind întâlnit în domeniul aplicațiilor mobile, fiind limbajul de bază pentru sistemul de operare Android. Pe lângă acestea mai este folosit în web sau embedded. Datorită mașinii sale virtuale (JVM) este folosit pentru back-end de majoritatea companiilor din Top 500.
Conform unor statistici efectuate de și pe platforma GitHub în 2018, Java se află pe locul 3 în topul preferințelor pentru dezvoltarea de aplicații.

/
Fig. 2 : Topul limbajelor populare în 2018 întocmit de GitHub[2]
2.2. Firebase
2.2.1. Platforma Firebase

Firebase face parte din categoria Backend-as-a-Service (BaaS) și este o platformă ce înglobează o multitudine de funcțiuni. Aceasta a apărut initițial sub formă de start-up, iar datorită potențialul și utilității uriașe a trecut sub tutela Google.

2.2.2. Consola Firebase
Consola este centrul prin care se controlează toate serviciile Firebase ale tuturor proiectelor înregistrate pe platformă. Funcționează ca un super administrator al serviciilor și necesită autentificare. După autentificare se selectează un proiect existent sau se adaugă unul nou. De asemenea, aici ne este oferită o privire de ansamblu asupra problemelor din aplicație prin Crashalytics sau statistici privind utilizatorii aplicației precum numărul de instalări, instalări active sau utilizatori activi într-o perioadă de timp. Tot aici putem vedea fluxul de date din aplicație, download-uri sau upload-uri în kilobiți.
Prin intermediul consolei putem observa și retenția utilizatorilor într-o anumită perioadă sau putem face profit de pe urma anunțurilor sau reclamelor în aplicație prin intermediul AdMob.

/
Fig. 3 : Firebase analytics și AdMob

BaaS permite dezvoltatorilor să se concentreze pe experiența utilizatorilor eliminând partea de server, partea de API-uri sau de stocare.
/
Fig. 4: O parte dintre serviciile oferite de Firebase

Serviciile Firebase folosite în cadrul aplicației : bază de date în timp real, Autentificare, depozitare Cloud.

2.2.3. Autentificare
Permite conectarea la o aplicație prin intermediul mai multor metode securizate. Metoda de autentificare aleasă pentru aplicație este cea cu email și parolă , accesul la date fiind permis doar până la un anumit nivel fără un cont valabil.
Firebase Auth are un sistem foarte avansat când vine vorba de fiabilitate pe dispozitive mobile. O dată ce un utilizator s-a autentificat de pe un dispozitiv acesta rămâne autentificat până la ștergerea aplicației sau ieșirea din cont.

/
Fig.5 : Consola pentru modurile de autentificare în aplicație

Activarea sau dezactivarea unor servicii de autentificare (sign-in) se face printr-un simplu buton în consola Firebase aferentă aplicației. În partea de cod aplicației lucrurile sunt asemănătoare indiferent de metoda de autentificare dorită : se apelează serviciul (API-ul) de autentificare cu credențialele introduse, iar mai apoi este verificată autenticitatea datelor cu serverul. Datele esențiale sunt trecute prin o funcție hash și sunt criptate, astfel încât nivelul de siguranță privind datele crește exponențial. Atât emailul, cât și hash-ul parolei pot fi văzute în cadrul consolei de administrator.
Pentru evitarea aglomerării aplicației cu conturi false sau nefolosite, dar și pentru evitarea anunțurilor false create de boți, la crearea unui cont trebuie confirmată adresa de email, acest lucru fiind posibil tot prin cadrul serviciului de autentificare amintit mai sus, oferit de Firebase.

2.2.4. Realtime Database
Este vorba despre baza de date a prezentului și viitorului, baza de date în timp real. Spre deosebire de bazele de date clasice în care se fac apeluri pentru sincronizarea datelor, această bază de date trimite datele către aplicație în momentul în care se înregistrează schimbări în interiorul nodurilor sale.
Principiile acestei baze de date sunt diferite de cele ale unei baze de date clasice : datele sunt stocate sub formă de noduri și copii în diverse tabele.

/
Fig.6 : Exemplu tabelă în Firebase, cea care se vede în detaliu fiind cea cu utilizatori

După cum putem observa în baza de date avem mai multe tabele, iar fiecare tabelă are mai multe noduri, numite copii. Luăm ca exemplu tabela de utilizatori “users” din imagine și exemplificăm modul de lucru cu Firebase Realtime Database. În primul rând trebuie să stabilim accesul la datele dinăuntrul tabelelor, acest lucru se face din consolă.
/
Fig.7 : Consola permisiunilor de acces la baza de date

În partea de cod trebuie să obținem o referință către baza de date, iar de acolo totul se desfășoară sistematic : este accesat fiecare nod în parte, de la nodul mare (tabela) “users”, mai apoi la ID-ul următoarei intrări și implicit ID-ul unui utilizator, iar la final putem accesa nodul mBookingManager. Acest lucru se face foarte ușor adăugând “ .child() “ de câte ori este nevoie la o referință către o bază de date.
După ce am stabilit comunicarea cu un nod / copil al tabelei putem accesa fiecare element / nod al său în parte cu un for each. Se face o snaphshot (poză) bazei de date și se accesează ia fiecare nod al snapshot-ului obținut în parte. Deoarece în aplicație lucrăm cu clase și obiecte, iar din Firebase datele vin sub alte forme trebuie să facem cast din tipul de dată respectiv în clasa care este defapt reprezentată. Pentru a putea face operațiile de mai sus trebuie să atașăm un “data listener” pe referința tabelei dorite. Există trei tipuri de “listeners”, în funcție de modul în care acestea se activează : doar atunci când se adaugă un nou nod în tabelă, o singură dată (ca un request / querry clasic), iar al 3 – lea nod este cel care aduce elementul de noutate în funcționalitate și utilitate – cel care se activează de fiecare dată când există o schimbare la oricare din datele existente în tabelă (adăugare, ștergere, modificare).
Pentru cel din urmă se folosesc două metode care ajută la prelucrarea datelor: onDataChanged() – pentru atunci când se modifică datele, onCancelled() – folosită de obicei atunci când nu se poate accesa baza de date (răspuns de la server) din diverse motive.

/
Fig. 8 : Exemplificare prelucrare de date din Firebase

Acestă tehnologie avansată este posibilă și cu ajutorul WebSocket-urilor. În timp ce în majoritatea cazurilor în lucrul cu baze de date se folosește protocolul de comunicare HTTP, Firebase folosește WebSocket-urile pentru faptul că acestea sunt cu mult mai rapide și mai ușor de întreținut deoarece la un singur socket se pot conecta o mulțime de endpoint-uri. Toate datele se sincronizează automat înăuntrul unei conexiuni de tipul celei amintite mai sus și viteza cu care se întâmplă depinde doar de viteza internetului.

2.2.5. NoSQL
Firebase folosește pentru bazele sale de date o tehnologie nouă, diferită de stilul clasic SQL, numită NoSQL. Toate datele sunt stocate sub formă de JSON. Putem oricând importa sau exporta datele sub formatul amintit mai devreme,
Bazele de date SQL sunt foarte puternice și versatile, folosite mai ales la interogări complexe. Ele sunt însă destul de restrictive necesitând ca toate datele salvate să urmeze o anumită schemă, ceea ce se traduce prin multă planificare în prealabil și o mare dificultate la modificare.
Pe de altă parte bazele de date NoSQL (non-SQL sau not-only-SQL) sunt mult mai flexibile, ele având posibilitatea stocării datelor în multe variante: bazate pe coloane, de tipul cheie-valoare, documente, graph-uri. Nu necesită o planificare amănunțită în prealabil și se pot adăuga tipuri de date pe parcurs. Acest tip de baze de date este folosit în aplicația WorldApp. În imaginea următoare avem o ilustrare a diferențelor dintre cele două variante de baze de date.

/
Fig. 9 : Comparație între SQL și NoSQL

2.2.6. Firebase Storage
Este o modalitate foarte simplă de a salva fișiere binare, cele mai uzuale fiind imaginile. În aplicația dezvoltată pentru licență am folosit acestă facilitate de stocare de fișiere (file storage) pentru imaginile de profil, ale tururilor și oportunităților de cazare. Pentru a vedea mai multe detalii despre un fișier se poate da click pe el și în partea dreaptă vor fi afișate. Printre aceste detalii regăsim și link-ul de download care este folosit mai apoi la afișarea imaginii în aplicație.
În momentul în care se adaugă o imagine nouă în serviciul de stocare Firebase facem un apel pentru a obține URL-ul imaginii și pentru a-l salva în nodul corespunzător. Pentru a afișa mai apoi imaginea respectivă se încarcă URL-ul salvat într-o componentă de afișare imagini (ImageView). Pentru a asigura unicitatea numelui unui fișier folosim funcția de obținere a timpului în milisecunde din Java.
/
Fig. 10 : Datele unui fișier stocat în serviciul de File Storage al Firebase

/
Fig. 11 : Exemplificare de stocare a unei imagini în FileStorage și Realtime Database

2.3. Google API
Pe lângă platforma Firebase mai există o multitudine de servicii oferite de Google. Dintre acestea, în aplicație am folosit Maps SDK pentru Android, adică Google Maps pentru platforma Android și Geolocation API.
Pentru a putea folosi serviciile mai sus amintite este nevoie de crearea unui cont de dezvoltator pe Google Cloud Platform. Această platformă este asemănătoare și conectată cu Platforma Firebase. Cele două sunt entități separe și împart informații până la un anumit punct, nefiind posibile totalitatea operațiilor Firebase din Cloud Platform și invers.
În timp ce Firebase este Backed-as-a-Service (BaaS), Cloud Platform este furnizor de servicii de tipul Infrastructure-as-a-Service (IaaS), Platform-as-a-Service, servicii de tipul “serverless computing”. De asemenea, este o platformă pentru dezvoltare și găzduire de Aplicații Web, Internetul Lucrurilor (IoT), are funcții variate de analiză și ramificații în multe alte arii și domenii.
/
Fig. 12 : Privire de ansamblu asupra platformei Google Cloud

Așa cum am menționat mai sus, este nevoie de un cont de dezvoltator pentru a putea folosi platforma, dar nu este suficient pentru a putea folosi o gamă largă de produse, printre care și Maps SDK sau Geolocation, prezente în aplicație. Pentru a putea fi folosite toate serviciile este necesară introducerea unui cont sau card bancar valabil, cu toate că pentru exersarea dezvoltării de aplicații folosind API-uri din platformă nu vom ajunge efectiv să plătim în primul an sau până la o anumită mărime de date.
Pentru a putea folosi un serviciu în aplicația din Android Studio (sau orice alt mediu de dezvoltare) trebuie să avem instalată extensia (Plug-in) Google Play Services, iar mai apoi fiecare serviciu în parte. Versiunile de servicii și Google Play trebuie să corespundă între ele, dar nu trebuie să fie neapărat aduse “la zi” dacă nu dorim schimbările din noile versiuni. După ce adăugăm serviciile în mediul de dezvoltare trebuie să creăm un proiect în Cloud Platform. În acest proiect vom avea toate serviciile folosite în cadrul proiectului, pentru fiecare proiect în parte fiind necesară activarea individuală a serviciilor și implicit introducerea unui nou cont bancar, fapt care ușurează munca dezvoltatorilor per ansamblu.
Odată ce avem serviciile instalate și activate va trebui să obținem datele de identificare pentru servicii și proiect : în Android Studio sunt trecute cheile primite de la Cloud Platform și care vor permite accesarea API-urilor, iar în Cloud Platform sunt trecute date despre proiect pentru a nu permite altor proiecte să solicite datele în numele nostru.

Google Maps API (Maps for Android SDK) este unul dintre serviciile disponibile pe Google Cloud Platform și utilizate în cadrul aplicației. Pentru simpla afișare a unei hărți este nevoie de cheia de autentificare amintită mai sus, iar apelurile către server și respectiv, costurile, se pot vedea în panoul principal al Consolei la secțiunea Google Maps. Pe lângă afișarea hărții prin intermediul API-ului putem pune marcaje pe hartă și putem obține direcții către acel marcaj prin 2 atingeri.

/
Fig. 13 : Privire de ansamblu asupra Serviciului Maps Android SDK din platformă

Geolocation API este un serviciu care vine în completarea celui mai sus amintit, dar poate fi folosit și separat. Prin intermediul acestuia putem afla locația dispozitivului sau putem extra date de pe hartă. De exemplu, la plasarea unui marcaj (pin) pe hartă putem obține țara, regiunea, localitatea, adresa exactă, codul poștal și alte date de la acea poziție.
Legătura dintre Firebase și Cloud Platform este foarte strânsă, ambele fiind servicii oferite de către Google : proiectele dintr-o consolă se regăsesc și în cealaltă atâta timp cât pe ambele console este autentificat un singur utilizator.

2.4. GitHub
Git reprezintă un sistem distribuit de control al versiunilor diferite de cod, adică de detectare a schimbărilor din codul sursă de la o variantă (versiune) la alta. Este folosit peste tot unde se efectuează acțiuni de dezvoltare de cod și scopul său este acela de a ușura munca dezvoltatorilor de cod, dar și aceea de a coordona mai bine munca în echipă asupra unui produs. Nu doar fișierele cu cod pot fi trecute printr-un sistem de genul celui amintit, ci orice tip de fișiere.
Printre calitățile Git se enumeră viteza, integritatea datelor și ajutorul în munca care nu este liniară și care necesită îmbinarea mai multor caracteristici diferite. De exemplu, în cadrul unei aplicații se poate lucra în paralel la autentificare și înregistrare, iar mai apoi codul este pus împreună și adus la o formă comună care să permită rularea sa în condiții optime. De asemenea, datorită sistemului distribuit de control ar versiunilor putem reveni oricând la o variantă anterioară celei curente dacă este nevoie.
Git este creat de către Linus Torvalds, tatăl sistemului de operare Linux și este open-source.
GitHub este o aplicație web pentru găzduirea unui sistem de control al versiunilor – Git. Este o subsidiară a celebrei companii Microsoft și oferă și servicii de identificare a problemelor din cod (bug-uri), cerere de noi caracteristici al sistemului dezvoltat, administrarea sarcinilor de lucru și altele. Probabil cel mai mare avantaj oferit însă de acest software este faptul că oferă o interfață mai prietenoasă cu utilizatorul pentru folosirea sistemelor distribuite de control al versiunilor.
Pentru a putea folosi GitHub trebuie să avem un cont, iar în cadrul unui cont putem avea mai multe proiecte, numite în limbaj de specialitate “repository” sau pe română – depozit. Aceste depozite sunt publice în opțiunea gratuită a aplicației, adică orice are acces la ele, însă nu pot adăuga sau modifica, ci doar vizualiza. Pentru a putea adăuga cod este nevoie de acces și de a inițializa repository-ul pe computerul dezvoltatorului care dorește să contribuie.
De obicei la un proiect mare contribuie multe persoane și acest lucru se poate vedea. Este foarte folositor deoarece așa se înțelege mai bine cum s-a ajuns la un produs final și cine a avut o anumită contribuție.
Pentru o utilizare mai facilă a sistemului de control al versiunilor putem folosi diferite programe care au o interfață prietenoasă cu utilizatorul privind operațiile posibile sau necesare : inițializare depozit (repository), creare de noi ramuri (branch-uri), aducerea codului de pe o ramură la zi și altele pe care le vom discuta în continuare. Pentru acest proiect eu am ales să folosesc aplicația GitKraken.
/
Fig. 14 : Exemplu de depozit (repository) pe GitHub

2.5. GitKraken
Acest software este gratuit dacă nu este folosit în scop comercial și este foarte intuitiv de folosit.
Operațiile folosite în GitKraken pentru dezvoltarea aplicației : inițializare repository, creare de ramură nouă, aducerea la zi a codului de pe ramura deviată din ramura principală, îmbinarea unei ramuri derivate cu ramura principală, revenirea la o versiune anterioară.
În figura de mai jos avem un exemplu de unire a unei ramuri derivate din ramura “master” cu fiecare “commit” (adăugare de versiune nouă, vizibilă doar local) și data la care a fost efectuat.
În momentul în care selectăm una dintre versiuni vom putea vedea toate modificările care au avut loc față de versiunea precedentă : adăugare, modificare sau ștergere.
Pentru a reveni la o versiune anterioară se apasă click dreapta pe versiunea dorită și se face o operațiune de inversare (derulare înapoi) a schimbărilor efectuate.

/
Fig.15 : Fereastra principală din GitKraken
/
Fig. 16 : Exemplu de modificări asupra unei versiuni de cod

În cazul în care ne dorim ca schimbările să ajungă și pe server, adică toată lumea care participă la proiect să le poată vedea trebuie să trimitem aceste modificări folosind opțiunea “push”.
Pentru fiecare nouă funcționalitate am creat o nouă ramură, derivată din ramura principală, iar la final am unit ramura nouă cu cea din care a fost derivată, adăugând noile funcționalități la cele existente.

2.6. Android Studio
2.6.1. Informații generale
Este mediul de dezvoltare oficial al sistemului de operare Android, oferit de Google. Construit pe ideea celor de la JetBrains – IntelliJ IDEA, a oferit o alternativă mult mai bună la dezvoltarea aplicațiilor mobile în Eclipse. Limbajele acceptate sunt Java și Kotlin – noul limbaj dezvoltat de cei de la Google.
Fiind un produs dezvoltat de Google folosirea Firebase și Cloud Platform în cadrul IDE-ului nu doar că este facilă, dar este chiar integrată prin diferite operațiuni de ajutor. Avem parte de un asistent pentru Firebase care ne ajută atât cu documentație pentru serviciile oferite direct în aplicație, cât și cu bucăți de cod suficiente și necesare pentru conectarea la un serviciu anume.
/
Fig.17 : Asistentul Firebase pentru Android Studio și o parte de cod

2.6.2. Manifest, Gradle
Fișierul Manifest este specific unui proiect nativ Android și conține informații despre numele proiectului, activitățile (ecranele) din cadrul proiectului, dar și permisiunile de care are nevoie aplicația pentru a funcționa așa cum este intenționat : acces la locația dispozitivului, acces la internet, acces la stocare internă pentru scriere și citire.
În fișierul Gradle.app sunt importate biblioteci sau proiecte care vin în ajutorul sau în completarea serviciilor existente deja în Android Studio, sau chiar funcționalități complet noi. Aceste biblioteci trebuie administrate cu grijă deoarece multe sunt interconectate și au diferite versiuni, iar pentru o funcționare corectă versiunile trebuie să coincidă. De exemplu, pentru biblioteca folosită pentru componenta RecyclerView trebuie să avem importată și biblioteca support:appcompat-v7.
Tot în cadrul fișierului Gradle avem trecute și versiunile de Android țintă și minime, dar și versiunea de Google Play Store a aplicației.

2.6.3. Activități și fragmente
O activitate reprezintă un ecran într-o aplicație Android, în timp ce un fragment reprezintă o porțiune de ecran. Mai multe fragmente pot face parte din aceeași activitate.
O activitate este formată dintr-un fișier Java în care este trecută partea de cod și un fișier XML unde sunt trecute componentele care sunt afișate pe ecran : butoane, casete de text, afișare de imagini, desfășurare de liste și multe alte componente. Cele două sunt legate între ele, iar pentru a avea acces la componentele XML în fișierul Java acestea trebuie declarate, iar mai apoi identificate după ID. Deși aproape toate proprietățile componentelor de interfață pot fi setate atât programatic, cât și în fișierul XML, unele sunt obligatoriu completate în cel din urmă amintit.
Pentru exemplificarea folosirii fragmentelor în cadrul unei activități vom lua activitatea de bază a aplicației WorldApp în care avem 4 fragmente diferite, schimbate între ele prin intermediul componentei Bottom Navigation care se află în fișierul XML aferent activității principale. Pe lângă această componentă, în activitatea principală folosim un element de tipul FrameLayout ca element de afișare al celor 4 fragmente. Prin apăsarea unui buton din cele 4 de jos, în FrameLayout este încărcat un fișier XML aferent fragmentului conectat la buton.
În imaginea de mai jos se poate observa faptul că în interiorul layout-ului principal există cele două componente amintite care schimbă fragmentele între ele pe ecranul principal.

/
Fig. 18 : Fișierul XML aferent activității principale

2.6.4. Emulatorul
Android Studio beneficiază de un simulator de dispozitive fizice numit emulator. Pe acesta de pot testa aplicațiile fără a fi nevoie de un dispozitiv fizic, dispunând de o mare varietate de dimensiuni și versiuni ale sistemului de operare. Însă, partea cea mai folosită de către dezvoltatori din acest emulator o reprezintă previzualizarea ecranelor. Această funcționalitate este prezentă în ecranul de editare al fișierelor XML și este de foarte mare ajutor deoarece ne arată aspectul activității în timp real.
În imaginea de mai jos putem observa faptul că am ales ca dispozitiv pentru emulator un telefon Galaxy Nexus 6P care este foarte similar ca dimensiune cu dispozitivul fizic pe care s-au efectuat testele. Deși un emulator este folositor, dezvoltarea profesionistă se face defapt folosind dispozitive fizice, acestea fiind mai rapide și având mai multe capacități tehnologice.

/
Fig. 19 : Previzualizarea unui ecran în timpul dezvoltării ecranului

2.6.5. Debug și Instant Run
Android Studio are posibilități de depanare a aplicație, fie ea instalată pe un dispozitiv fizic sau unul virtual, cu ajutorul emulatorului. Pentru a opri execuția în anumite puncte sau pentru a vedea valoarea unor variabile trebuie să folosim bine-cunoscutele breakpoint-uri și trebuie să activăm modul “Debug” din aplicație.
Pentru mai multe detalii asupra execuției programului sau a erorilor apărute avem la dispoziție ferestrele Logcat, Debug sau Run unde vedem detaliat ce se întâmplă. Mai există și posibilitatea captării și tratării de eroare în timp real folosind instrucțiunile Try și Catch.
O caracteristică foarte folositoare a opțiunii de depanare din cadrul Android Studio este afișarea valorilor tuturor variabilelor care fac subiectul execuției la momentul depanării, pe ecran.
/
Fig. 20 : Android Studio în modul Debug

Funcția de Instant Run a mediului de dezvoltare permite încărcarea schimbărilor foarte rapid, economisind foarte mult timp, această funcție nefiind prezentă în multe alte medii asemănătoare. Este o practică de dezvoltare incrementală și înseamnă defapt încărcarea schimbărilor în loc de reîncărcarea întregii aplicație, proces care de obicei poate dura și minute bune, se transformă în cateva secunde.

/
Fig. 21 : Modul de operare al funcției Instant Run [3]

3. IMPLEMENTAREA APLICAȚIEI
3.1. Arhitectura aplicației
Software-ul are două mari module : aplicația mobilă și baza de date Firebase. Prin intermediul aplicației mobile sunt introduse și prelucrate date, ele fiind scrise și citite din baza de date. Prin intermediul Firebase se asigură nu doar baza de date, ci și serviciile de autentificare și înregistrare.
În WorldApp un utilizator cu un cont valabil poate fi în același timp călător și gazdă. Din ecranele de listare servicii poate alege ceea ce alți utilizatori oferă, iar din ecranul Profile poate alege să vadă serviciile oferite de către el, respectiv să adauge unele noi.

/
Fig. 22 : Arhitectura aplicației

3.2. Schemă bloc funcțională
În figura 23 este prezentată schema bloc funcțională pentru operațiunea de rezervarea a unui serviciu existent în cadrul aplicației. Pentru rezervarea unui serviciu este nevoie de autentificare.
/
Fig. 23 : Schema bloc funcțională pentru o rezervare

3.3. Utilizarea limbajului Java

Un proiect în Android Studio conține o mulțime de fișiere, dar cele cu care lucrăm în mod direct sunt cele Java și XML. În acest paragraf vom discuta despre fișierele Java.
Orice activitate (ecran) al aplicației este o combinație între un fișier Java în care este trecută partea de cod și un fișier XML unde sunt trecute componentele de interfață. Legătură dintre ele se face din ambele fișierele, în cel Java declarându-se fișierul XML care conține layout-ul dorit, iar în celălalt fișier este declarată clasa care reprezintă defapt activitatea.

/
Fig. 24 : Declararea fișierului XML în clasa aferentă unei activități

/
Fig.25 : Declararea clasei în fișierul XML

Pe lângă activități (ecrane) fișierele Java reprezintă clasele care abstractizează nevoile aplicației, numite modele, clase de ajutor pentru conectare la baze de date, conversii între tipuri de date, clase de ajutor pentru rezervare sau înregistrare.

3.4. Elemente XML și resurse
Pentru fișierele de genul există un folder separat denumit res în cadrul unui proiect în Android Studio. În cadrul acestui folder există sub-folderele : drawable, layout, menu, mipmap, values.

/
Fig. 26 : Folderul res

În folderul mipmap se găsesc fișiere de același tip cu numele folderului, de obicei create în interiorul proiectului și reprezintă imagini.
În interiorul folderului values sunt salvate fișiere XML cu date despre stiluri, culori, texte implicite, dar și create de către dezvoltator. În cadrul aplicației am folosit mai multe stiluri personalizate pentru a reduce timpul de dezvoltare prin eliminarea liniilor de cod repetitive, dar și deoarece pentru unele elemente de UI, de exemplu butonul rotund, era nevoie de un stil nou.

/
Fig. 27 : Exemplu de stil din fișierul styles.xml

Un alt exemplu foarte folositor în reprezintă fișierul strings.xml în care se salvează cuvinte sau fraze care nu se modifică pe parcursul rulării fiind posibilă refolosirea acestora prin referirea locației si numelui – @strings/save_text, spre exemplu. O altă misiune a fișierului este aceeea de a facilita traducerea aplicației în mai multe limbi deoarece aceleași cuvinte în limbi diferite vor avea același ID.
În folderul home se află doar layout-ul pentru meniul din josul ecranului principal.
Folderul în care se află toate fișierele XML care reprezintă layout-urile aferente ecranelor este folderul layout. Aceste fișiere XML reprezintă atât ecranul întreg, cât și componente separate cum ar fi șabloanele pentru RecyclerView, diferitele tipuri de bări de navigare sau fragmente.
Un fișier layout are la bază un “container”, un view părinte care să conțină toate celelalte view-uri prezente în fișier. Un view reprezintă un element de UI. Există și view-uri care sunt formate din alăturarea mai multor view-uri, de exemplu ScrollGalleryView.
/
Fig.28 : Exemplu de ecran în XML
În ecranul de mai sus este folosit un RelativeLayout ca view principal, iar view-urile copil sunt bara de navigare, un ImageView folosit pentru afișarea de imagini, 3 casete text și un buton. Bara de navigare este un layout separat, un fișier XML separat, iar pentru a fi folosit ne ajutăm de expresia <include layout=”@layout/numele_fișierului_xml_dorit”/> care selectează fișierul denumit și îl introduce în layout-ul curent. Astfel crește foarte mult re-utilizabilitatea componentelor și se face depanarea mult mai ușoară și dezvoltarea mai rapidă.
/
Fig. 29 : Fișierul XML care conține bara de navigare
Un element de UI are mai multe atribute care pot fi setate pentru a determina diferite caracteristici, cele mai importante fiind lățime, lungime și poziția în pagină.
/
Fig.30 : Exemplu de view -casetă text, cu diferite atribute

Folderul drawable conține imagini, clip art-uri (icoane) create în cadrul Android Studio, dar și
diferite fișiere XML care conțin stiluri pentru butoane sau pentru BottomNavigation. În principiu aici sunt stocate fișiere ce țin de aspect, UI diferite de layout-uri sau stiluri.

/
Fig. 31 : Trei tipuri diferite de fișiere în folderul drawable
3.5. Activități Android
În cadrul unei clase aferente unei activități sunt declarate toate elementele de UI (User Interface – interfața cu utilizatorul) care fac subiectul unor schimbări sau a unor valori care sunt dinamice, adică sunt schimbate programatic. Ele se identifică după un nume (ID) care trebuie să fie unic, iar pentru a putea accesa un element acesta trebuie declarat și găsit prin metoda findViewById() care are ca parametru numele.
O clasă care definește o activitate trebuie să moștenească o clasă oferită de către IDE, și anume AppCompatActivity. În interiorul acestei clase există mai multe metode care gestionează ciclul de viață al unui ecran :
onCreate()
onStart()
onResume()
onPause()
onStop()
onDestroy()
onRestart()
/
Fig. 32 : Ciclul de viață al unei activități [4]

În mod obișnuit declarațiile de componente XML se efectuează în metoda onCreate() care este suprascrisă din clasa de bază amintită mai sus. În momentul în care dorim să efectuăm operații pe una dintre metodele din cadrul ciclului de viață al unei activități trebuie să supra-scriem
În cadrul aplicației am creat o nouă activitate de bază care moștenește AppCompatActivity și care reprezintă scheletul pe care sunt construite majoritatea activităților. Această clasă se numește BaseAppCompat și aduce în plus față de cea amintită mai sus un “toolbar”, o bară de navigație, cu un buton care ne întoarce la ecranul precedent și cu o metodă care ne oferă posibilitatea de a seta un titlu pentru această bară, dacă dorim. Acest lucru ușurează munca și scade din timpul efectiv de dezvoltare. Ne oferă și o flexibilitate prin faptul că putem alege ce metode să folosim în fiecare situație.

/
Fig. 33 : Clasa de bază pentru majoritatea ecranelor din aplicație
Pe lângă elementele de UI și operațiile asupra lor, într-o activitate mai sunt declarate operații de prelucrare a datelor, de primire sau de trimitere a lor către server sau utilizatori.
Activitățile de autentificare și înregistrare
În cadrul activității de autentificare utilizatorul introduce datele de autentificare, iar la apăsarea butonului “Log In” se efectuează autentificare prin serviciul de autentificare al Firebase. În imaginea de mai jos observăm posibilitatea de înregistrare în cazul în care nu avem un cont valabil.
/
Fig. 34 : Ecranul de autentificare

Datele introduse în casetele pentru numele de utilizator și parolă sunt preluate și apelează metoda de autentificare cu email și parolă din Firebase, care are și un “listener” care anunță aplicația când datele au fost verificate cu cele din baza de date, iar în cazul în care operația a avut succes se va continua operația de autentificare. Un ultim pas pentru această operație este verificarea email-ului la momentul înregistrării unui cont nou prin funcția isEmailVerified(). Acest email se trimite automat și contul trebuie confirmat o singură dată. Măsura a fost luată pentru prevenirea creări de conturi false sau a spam-ului.
/
Fig.35 : Operațiile de autentificare din clasa LogInActivity
Înregistrarea unui cont nou se face din activitatea RegisterActivity, la care se ajunge prin activitatea LogIn. Sunt necesare doar câteva informații de bază, fără de care sistemul gândit nu ar putea funcționa, dar care să nu fie prea în detaliu și oamenii să abandoneze înscrierea în aplicație.
/
Fig.36 : Ecranul de înregistrare

La fel ca la procesul de autentificare și în procesul de înregistrare avem funcții și “listeners” din cadrul Firebase, acesta fiind unul dintre motivele pentru care am ales această platformă pentru dezvoltarea software-ului. Două dintre funcțiile oferite de serviciul de înregistrare mai importante, apelate, sunt createUserWithEmailAndPassword() unde trimitem ca parametrii numele de utilizator și parola introduse de utilizator și sendEmailVerification() care trimite un mail de verificare a contului către email-ul introdus.

/
Fig. 37 : Cod pentru înregistrarea în Firebase

Splash Activity – este activitatea care încarcă principalele date din baza de date în memoria telefonului. Este afișată înaintea activității principale pe durata efectuării operațiilor de verificare și preluare de date.
Home Activity – activitatea principală, afișată la intrarea în aplicație, după preluarea datelor necesare. Această activitate este compusă din 4 fragmente : Home, Trips, Inbox, Profile, care sunt încărcate în funcție de butonul apăsat în josul ecranului.
Pentru bara de jos am folosit un element numit BottomNavigationView care conține 4 elemente, fiecare cu un ID propriu. În funcție de ID-ul selectat se va încărca un fragment diferit în ecranul principal. Toate fragmentele sunt declarate la inițializarea ecranului Home pentru optimizare.
Home Fragment – sunt afișate opțiunile de vizualizare a tururilor ghidate, oportunităților de cazare, locurilor de parcare sau a statisticilor.
Pentru afișarea opțiunilor am folosit elementul de UI numit CardView și am setat pentru fiecare dintre ele metoda onClick() pentru a ne duce la ecranul aferent informației afișate. CardView oferă un aspect mai plăcut el fiind creat pentru astfel de expunere de informații.
Trips Fragment (fig.39) – are două funcționalități: afișarea tuturor serviciilor pe care le-am contractat sau am încercat să le contractăm sub denumirea de “Your bookings”, iar sub denumirea de “Your guests” sunt afișate toate serviciile oferite de către utilizatorul curent care au fost contractate și acceptate. Cea din urmă are scopul de a ajuta în administrarea afacerii din punctul de vedere al unei gazde.

/
Fig. 38 : Încărcarea diferitelor fragmente în funcție de opțiunea selectată

/
Fig. 39 : Ecranul pentru fragmentul Trips

Inbox Fragment – sunt afișate toate serviciile noastre contractate în diferite perioade de către alte persoane. Există opțiunea de acceptare sau respingere, se pot vedea aproximativ aceleași detalii despre rezervare ca în fragmentul Trips.

/
Fig. 40 : Ecranul pentru fragmentul Inbox

Dacă în ecranul Inbox vedem banii încasați de către proprietar, în ecranul Trips vedem banii plătiți de către client.
Profile Fragment – în cazul în care nu suntem autentificați va apărea un buton care ne va duce la ecranul de autentificare. De asemenea, în acest caz sunt permise toate operațiile, mai puțin cele de rezervare servicii, iar ecranele Trips și Inbox vor fi goale.
În cazul în care există un utilizator autentificat, atunci vor apărea serviciile propuse de acesta într-un meniu care mai are opțiunile deconectare de la cont, ștergere cont și altele clasice. În plus va mai apărea în partea de sus o bară cu numele și poza utilizatorului curent. Poza se poate schimba oricând prin simpla apăsare în locul unde este localizată aceasta.
/
Fig. 41 : Ecranul pentru profil

La apăsarea opțiunii de ștergere cont va apărea un dialog care va întreba utilizatorul dacă este sigur că dorește să facă acest lucru. În cazul unui răspuns afirmativ atunci datele utilizatorului din aplicație vor fi șterse.
Cele trei activități pentru afișarea serviciilor oferite de utilizator: Accommodation, Tour și Parkings listings sunt foarte asemănătoare, ele având în spate aceeași logică, dar tabele (date) și adaptor pentru componenta RecyclerView diferit.
RecyclerView este o componentă asemănătoare unei liste, mult mai capabilă însă : se poate actualiza în timp real, afișează oricâte obiecte, calculează singură dimensiunile de care are nevoie, se poate personaliza și adapta ușor. În fișierul XML se declară ca o componentă obișnuită, este însă nevoie de o bibliotecă separată care se importă în fișierul Gradle -mai multe detalii despre acest lucru în sub-capitolul dedicat.
/
Fig. 42 : RecyclerView în fișierul XML

În fișierul Java se declară ca un element obișnuit XML, dar pentru a funcționa trebuie să se seteze un adaptor și eventual un Layout Manager. De fiecare dată când setăm adaptorul se apelează metoda setAdapter() și se pasează ca argument tipul de adaptor.
Pentru fiecare listă în parte se poate crea un adaptor separat dacă modul în care sunt afișate datele din listă este diferit. În soluția prezentată un adaptor are în constructor doi parametrii : contextul (activitatea) de unde este apelat și lista de obiecte care trebuie afișată.
În adaptor setăm layout-ul pentru obiectele din listă , luăm valorile din listă și le atribuim elementelor de UI. Mai exact în metoda onCreateViewHolder() se setează șablonul pentru afișare, în metoda onBindViewHolder() se ia fiecare obiect în parte folosind metoda get() și parametrul position din metoda amintită pentru a determina obiectul în cauză. Tot în cadrul metodei de “bind” se setează și acțiunea care corespunde unui click pe celulă, deoarece e nevoie de parametrul position mai sus amintit pentru a determina care celulă a fost selectată.
/
Fig. 43 : onBindViewHolder() exemplu

Pentru exemplificarea unui ecran în care sunt listate servicii oferite de către utilizator vom folosi opțiunea “Tour listings” din meniul ecranului “Profile”, amintit mai sus.
/
Fig. 44 : Ecranul Tour Listings și exemplu de folosire a RecyclerView

Tot în cadrul acestui ecran este folosit și Floating Action Button, în josul ecranului în partea dreaptă. Este un element relativ nou și un acord nescris legat de el este acela că se folosește la adăugare. În cazul acesta îl folosim pentru adăugarea unui tur nou, iar în celelalte activități la adăugare de cazare, respectiv parcare. Particularitatea sa este că tot timpul se află deasupra celorlalte elemente de UI, în rest el fiind declarat și folosit ca un buton obișnuit.
Dacă apăsăm pe o pe o celulă se va deschide o nouă activitate, în care este desfășurat un tur, o cazare sau o parcare. Fiecare dintre cele 3 ecrane sunt diferite, având nevoie de detalii și mod de expunere diferite. La apăsarea unei celule toate datele sale vor fi serializate în format JSON, iar în următoarea activitate se de-serializează folosind, schimbul de date are loc cu ajutorul unei chei de identificare a valorii, un dicționar.
Pentru exemplificarea unui ecran care arată detaliile unui serviciu vom folosi un serviciu de cazare, fiind cel mai amplu având, printre altele : Google Maps unde avem locația clădirii și o galerie de poze.
/
Fig. 45 : Ecran al unei unități de cazare

Pentru galeria de poze s-a folosit o bibliotecă externă [] și s-a adaptat la cerințele aplicației. Pe fundal gri sunt expuse de la stânga la dreapta : numărul maxim de persoane acceptate, numărul de camere, numărul de paturi, iar la final avem numărul de băi. În fig. 45 se observă faptul că în captura de ecran este surprinsă tranziția între două imagini care fac parte din galerie. De asemenea, toate imaginile din galerie sunt vizibile în format de miniatură în partea de jos a imaginilor mari din galerie.
În figura următoare pe fundal gri este trecut proprietarul serviciului, iar sub harta care arată locul de cazare este trecută locația exactă.
/
Fig. 46 : Partea de jos al unui ecran aferent unei unități de cazare

Harta este interactivă și se poate mișca pentru a vedea ce este în jur, sau se poate apăsa pe pin-ul roșu și vor apărea cele 2 opțiuni din partea dreaptă a ecranului : amândouă reprezintă direcții de la locația curentă la locația de pe hartă.
Acționarea butonul de cumpărare “Book” funcționează doar dacă există un utilizator autentificat și a selectat o perioadă pentru rezervare. Dacă aceste condiții sunt îndeplinite atunci vom ajunge la ecranul de plată.
Payment Activity – în această activitate se introduc datele finale pentru efectuarea unei rezervări și este afișată suma care trebuie plătită.
Pentru verificarea datelor este folosită o funcție care asigură un minim de ajutor și care verifică lungimea caracterelor introduse în casetele corespunzătoare numărului de pe card, a CVV-ului sau a datei de expirare.
Sumele din partea de jos a paginii sunt preluate cu ajutorul unui BookingManager, nefiind nevoie să memorăm obiecte extra sau mai mari, salvând memorie și timp.
/
Fig. 47 : Ecranul pentru efectuarea plății și finalizarea rezervării

Administrarea rezervărilor
Este făcută cu ceea ce am denumit ca fiind BookingManager, un model care reține datele esențiale pentru afișarea și gestionarea unei rezervări.
/
Fig.48 : Clasa BookingManager
Sunt trecute mai multe date decât sunt necesare unui tip de rezervare deoarece acest manager este folosit pentru toate tipurile. Acest obiect se construiește treptat, adăugând date pe măsură ce avansăm în aplicație.

/
Fig. 49 : Exemplu de folosire a unui BookingManager

Există o tabelă în baza de date pentru BookingManager în care se scrie de fiecare dată când se efectuează o operație de rezervare. Mai apoi se citește când este nevoie să se afișeze o cerere de rezervare pentru un serviciu sau când trebuie afișată starea unei rezervări – ecranele Trips și Inbox.
Pentru schimbarea stării unei rezervări se modifică valoarea variabilei mStatus din nodul aferent ID-ului rezervării prin metoda updateChildren() din biblioteca Firebase.

Listarea tuturor parcărilor, tururilor sau a locurilor de cazare se face în cadrul a trei activități foarte asemănătoare, dar cu adaptor diferit, care se accesează prin intermediul CardView-urilor din activitatea principală, Home. Luăm ca exemplu listarea și filtrarea tururilor.
O primă filtrare se face după ID-ul proprietarului, fiind ilogic ca cineva să încerce să cumpere propriul tur ghidat. Pentru prevenirea erorilor s-au acoperit și cazurile în care nu există un utilizator autentificat, iar acesta va vedea toate tururile și va putea folosi și filtrele, dar nu va putea cumpăra până după autentificare.
Pentru ecranele de listare bara de navigare s-a schimbat în sensul în care apare și un buton care va deschide activitatea de filtrare. Între cele două activități se va face un schimb de informații, cele introduse în cea de filtrare vor determina care dintre tururile existente vor fi afișate, iar la o altă apăsare a butonului de filtrare se vor afișa valorile după care s-a făcut filtrarea anterioară pentru o mai bună experiență de utilizare.
/
Fig. 50 : Ecran pentru listarea tururilor disponibile după filtrare

/
Fig. 51 : Ecran de filtrare pentru tururi
Datele pentru filtrare sunt trimise sub forma unui tablou de valori care este desfășurat și interpretat în activitatea de listare. Pentru trimiterea valorilor spre folosire trebuie să apăsăm butonul din dreapta-jos care are o lupă, pentru a face aplicația mai intuitivă, și care ne va duce la ecranul de listare cu valorile dorite în tabloul menționat. Apăsarea butonului din bara de navigație nu va însemna o nouă filtrare.
În bara de navigare se va afișa tot timpul pasul curent pentru a facilita navigarea prin aplicație.
Din ecranul care listează un tip de servicii oferite de către utilizator prin Floating Action Buton-ul amintit mai sus se ajunge la o serie de ecrane care permite introducerea de noi oportunități. Desigur, de pe ecranul cu tururi vom putea adăuga noi tururi, de pe ecranul cu oferte de cazare vom putem pune noi locații, iar din cel de parcări vom adăuga noi locuri de parcare.
Pentru exemplificare vom lua în calcul adăugarea unei noi locații de cazare, fiind cea mai amplă operațiune dintre cele trei posibile.

/
Fig. 52 : Primul ecran pentru adăugarea unei case

Toate datele sunt adăugate într-o variabilă statică, foarte asemănătoare cu modelul obiectului care se vrea a se adăuga. În fiecare ecran se adăugă noi informații, iar dacă se dorește să se modifice pe parcurs se poate merge pe ecranele anterioare folosind butonul de navigare din bara de navigare.
Din primul ecran se preia numele anunțului dintr-o casetă Edit Text, pentru celelalte date afișate pe ecran folosindu-se un element numit “Spinner “care este varianta de casetă de tip “dropdown” bine-cunoscut în domeniu. Valorile din spinner trebuie inițializate cu un ArrayAdapter<Obiect> unde Obiect reprezintă tipul de date care va apărea în listă, iar pe lângă această listă în adaptor se declară și layout-ul, adică modul în care vor fi afișate aceste date.
/
Fig.53 : Folosirea variabilei statice în al 2-lea ecran de adăugare cazare

/
Fig.54 : Al 2-lea ecran de adăugare cazare
În cel de-al doilea ecran de cazare se stabilește locația pe hartă printr-o simplă apăsare pe hartă. S-a folosit un serviciu de stabilire a locației, care este vizibil în figura 54. La fiecare poziționare a pin-ului pe hartă, adică la fiecare apăsare pe hartă se vor completa datele găsite în zonă : în prima casetă text va fi trecută toată adresa, în a doua casetă este codul poștal, iar în ultima, județul sau regiunea administrativă. În cazul în care nu sunt corecte datele sau nu sunt completate deoarece nu au fost găsite utilizatorul va putea să introducă manual datele sau să le modifice pe cele existente.
Acest mod interactiv de introducere a datelor salvează timp semnificativ în momentul adăugării unui nou obiect, iar adresa poate fi exactă având inclusiv numărul și numele locației.

/
Fig.55 : Al 3-lea ecran de adăugare cazare

În cel de al 3-lea ecran se salvează în 2 casete text, dintre care una are proprietatea inputType="textMultiLine" pentru a permite o afișare pe mai multe rânduri. Pentru a nu acoperi tot ecranul se folosește și proprietatea “lines” cu valoarea 8, în eventualitatea în care vor fi mai mult de 8 rânduri introduse atunci se va putea mișca caseta text în sus sau în jos.

/
Fig. 56 : Ecran 4 înainte de adăugare poze

În ultimul ecran se vor încărca pozele în Firebase și vor fi afișate pe ecran după încărcarea lor. Pentru acest lucru este necesar ca la apăsarea butonului “Upload photos” să se deschidă o galerie cu poze. Acest lucru este posibil prin deschiderea unui ecran nou care să arate fișiere de tipul “image/*”, adică orice tip de imagini. Proprietatea “extra_allow_multiple” face posibilă selectarea mai multor imagini, iar “action_get_content” specifică faptul că se dorește să se extragă conținut din noua activitate.

/
Fig.56 : Modalitatea de a deschide o galerie și de a selecta mai multe imagini

O activitate de tipul celei de sus este exemplificată în următoarea figură. Observăm că interfața este una prietenoasă : în partea stângă este afișat numărul de imagini selectate, iar în dreapta este butonul de salvare.
/
Fig.58 : Activitatea de selectare poze pentru cazare

După selectarea imaginilor se obține calea către unde acestea sunt salvate în telefon într-un vector care salvează obiecte de tip “Uri”. Mai apoi se ia fiecare URI (Uniform Resource Identifier) din vector și se efectuează operații de urcare în serviciul Firebase Storage. Pentru a evita suprascrierea datelor la salvarea unor imagini noi se folosește aceeași metodă ca la salvarea imaginilor de profil : pentru fiecare fișier local care se dorește a fi urcat în Storage se schimbă numele curent cu data și ora curentă în milisecunde și se adăuga extensia originală. Această metodă este folosită în multe aplicații fiind foarte greu să se obțină două fișiere cu nume și extensii identice.
Următorul pas este acela de a urca fiecare imagine în Storage, iar mai apoi de a obține URL (Uniform Resource Locator) și salvarea sa în lista obiectului de cazare. Fiecare obiect are o astfel de listă care memorează locațiile din Storage , prin link-uri de descărcare, a imaginilor care aparțin lui.
Un ultim pas pentru completarea ecranului îl reprezintă afișarea imaginilor după ce au fost încărcate în serviciul Firebase cu ajutorul unui RecyclerView.
Butonul “Finish” va completa ultimele date necesare pentru adăugarea unui nou serviciu de cazare, va duce utilizatorul la ecranul principal și va afișa un mesaj de succes.
/
Fig. 59 : Ecranul 4 după încărcarea pozelor selectate

Noul obiect se poate vedea imediat după adăugare în lista de servicii oferite sau în ecranele de căutare de astfel de servicii.
3.6. Biblioteci utilizate
Toate biblioteci folosite se găsesc într-un singur fișier : build.gradle (modulul aplicație).
Fiecare linie înseamnă o nouă bibliotecă, cele subliniate cu galben semnalând faptul că există noi versiuni disponibile. La trecerea cu mouse-ul peste aceste linii galbene vor fi afișate versiunile mai noi disponibile. Actualizarea se face relativ ușor fiind necesar schimbarea numărului sau numelui bibliotecii respective, iar apoi se apasă butonul de sincronizare. Trebuie avut însă grijă la bibliotecile care țin de alte biblioteci sau care au referințe în alte biblioteci deoarece versiunile trebuie să fie compatibile.
Orice aplicație are câteva biblioteci inițiale, de bază, fără de care nu poate funcționa, cum sunt appcompat -v7, support-v4 sau support-compat.
În continuare vom discuta despre bibliotecile care aduc funcționalități extra, fie ele oferite de furnizori cunoscuți precum Google sau de diverși dezvoltatori din mediul online. Astfel de biblioteci se găsesc sub formă de proiecte în Repository-uri răspândite pe internet sau pe site-uri oficiale.
/
Fig. 60 : Fișierul gradle cu toate bibliotecile folosite

Bibliotecile Firebase : Core, Auth, Database, Storage. De biblioteca Firebase Core este nevoie în orice aplicație care folosește serviciile platformei cu același nume, indiferent de serviciu.
Biblioteca Firebase Auth este folosită pentru serviciile de autentificare din platformă și conține, printre altele, funcțiile signInWithEmailAndPassword() pentru autentificare și pentru înregistrare, createUserWithEmailAndPassword().
Firebase Database oferă accesul la serviciile de bază de date în timp real și conține funcțiile care efectuează operațiile. De asemenea, această bibliotecă este responsabilă și pentru “listeners”, cei care supraveghează tot ce se întâmplă cu baza de date.
Firebase Storage ne permite să operăm cu baza de date de tip blob, destinată stocărilor de obiecte de dimensiuni mari, de exemplu, poze.
Bibliotecile pentru lucrul cu imaginile sunt Glide și ScrollGalleryView. Glide este recunoscută la un nivel mai oficial în timp ce cea de a doua este preluată de pe GitHub și are la bază Glide, dar și alte două biblioteci asemănătoare. Dacă prima dintre cele amintite se ocupă de o singură imagine, cea de a doua reprezintă o galerie de imagini și chiar și video-uri.
Pentru a putea folosi hărțile am introdus biblioteca play-services-maps și un plug-in pentru Google Services.
Restul bibliotecilor sunt biblioteci comune pentru o aplicație Android, inclusiv gson pentru serializarea și deserializarea în format Json.

3.7. Bază de date
Baza de date este făcută pe platforma Firebase, serviciu despre care am discutat mai la începutul lucrării. Este un mijloc modern și practic de lucru pentru un volum mare de date și de tipuri diferite. Se bazează pe noduri și fișiere de tip Json.
JSON este un format ușor pentru schimbul de date, ușor de scris și de citit pentru oameni. Este un format foarte răspândit ce se bazează pe cheie-valoare.
/
Fig. 61 : Exemplu de JSON din baza de date
Ce este special la această bază de date este faptul că modificările sunt urmărite în permanență și atunci când are loc una se modifică și în locurile unde e relevant. Astfel se reușește afișarea imediată a unor servicii noi adăugate fără interogări asupra bazei de date.
Chiar și modul de interogare este diferit datorită nodurilor : nu se poate selecta o singură coloană după care să se caute, ci se iau obiectele la rând, se transformă din format JSON în obiect de tipul căutat și apoi se fac operații.

/
Fig. 62 : Tabelele din baza de date

Tocmai faptul că se bazează pe format JSON ajută la stocarea datelor fără a se pune problema de ce tip de date stocăm, problemă care se ridică la o bază de date de tip SQL. Toate datele sunt salvate sub formă de șiruri de caractere, iar mai apoi, la deserializarea lor în cadrul aplicației ele sunt convertite la un obiect dorit. În acel moment pot apărea probleme în cazul în care căutăm chei, adică nume de coloane, care nu există.
Identificarea în această bază de date se face, de regulă, după o cheie unică, un ID.
Pentru a scrie sau citi dintr-o anumită tabelă trebuie să obținem o referință către acea tabelă, acest lucru îl putem face prin următoarea linie de cod în cadrul unei clase Java : FirebaseDatabase.getInstance().getReference(). După ce se obține referința putem adresa diferite noduri adăugând secvența .child("Nume nod") la final, iar dacă dorim să ajungem la nivel mai jos în nod adăugăm secvența .child("Nume nod") ori de câte ori este nevoie, până la nivel de coloană. Este o bază de date în timp real datorită “listeners” – funcție care observă ce se întâmplă cu valorile din tabelă și trimit mai apoi schimbările sau chiar toată tabelă, după cum este cazul.

/
Fig. 63 : Exemplu de folosire a unui listener

Pentru a veni cât mai bine în întâmpinarea cerințelor unui dezvoltator există trei tipuri de astfel de funcții : ValueEventListener – observă și trimite date la fiecare modificare din tabelă, SingleEventListener – preia și trimite datele o singură dată, ChildEventListener – preia și trimite date doar la adăugarea unui nou nod în tabelă. În cadrul aplicației au fost folosite primele două.
Preluarea datelor se face în metoda onDataChange(DataSnapshot dataSnapshot) unde DataSnapshot reprezintă un fișier JSON cu datele din tabelă la momentul interogării. De obicei se parcurg toate obiectele din snapshot și se convertesc la clasa dorită prin metoda getValue(Clasa.class), parametrul fiind clasa respectivă.
Pentru a scrie în baza de date se memorează toate datele într-un obiect, iar mai apoi folosind funcția setValue(), cu parametrul fiind respectivul obiect, pe o referință către o tabelă se va crea un nod nou reprezentând obiectul în format JSON.
Există și varianta de modificare a unor date existente, acest lucru făcându-se prin intermediul funcției updateChildren() care va primi ca parametru un obiect de tip HashMap care va avea cheia numele coloanei, iar valoarea va fi cea pe care dorim să o înlocuim în baza de date.
Firebase Storage funcționează foarte asemănător Database, fiind și aici nevoie de o referință către locația unde se salvează date. Aici însă se creează foldere și fișiere, nu noduri. Deoarece fișiere salvate aici sunt considerabil mai mari față de cele din baza de date se folosește un task de tipul StorageTask specific Firebase pentru a asigura integritatea datelor și faptul că acestea ajung la destinație.

/
Fig.64 : Modul de organizare în Firebase Storage

/
Fig. 65 : Exemplu de utilizare a Firebase Storage

În imaginea de mai sus se observă task-ul despre care s-a discutat și clasicul CompleteListener() specific Firebase care se execută la încheierea cu succes a unei operații legate de baza de date.
3.8. Testarea aplicației
În dezvoltarea aplicațiilor mobile este o bună practică testarea în timp real, pe dispozitiv fizic. Acest lucru este și foarte ușor de efectuat fiind vorba despre un dispozitiv care este tot timpul la îndemână și nu despre o antenă radio, de exemplu.
La fiecare ecran nou, la fiecare nouă funcționalitate dezvoltată sau la orice schimbare s-a făcut testarea pe dispozitiv fizic. Funcția Instant Run menționată anterior este creată special pentru a permite astfel dezvoltarea iterativă, în care se văd ușor greșelile și se corectează repede. Erorile au fost eliminate astfel imediat când au apărut. Pentru fiecare nouă funcționalitate s-a creat o ramură nouă pe Git, iar la final, după dezvoltarea și testarea ecranului s-a unit cu celelalte funcționalități.
Au fost efectuate teste și de către alte persoane, care nu erau familiare cu aplicația, iar în urma sugestiilor venite din partea lor am îmbunătățit experiența utilizatorului.

4. UTILIZAREA APLICAȚIEI
4.1. Cerințe de sistem
Aplicația funcționează doar pe dispozitive mobile care rulează ca sistem de operare Android. Este necesară o versiune Android 6.0 sau mai nouă și minim 20MB memorie. Aceste specificații permit rularea aplicației pe cel puțin 70% dintre dispozitivele ce au acest sistem de operare.

/
Fig. 66 : Distribuția telefoanelor după versiunea sistemului de operare, mai 2019[5]

Pentru funcționarea aplicației este nevoie de o conexiune stabilă la internet.
4.2. Pornirea aplicației
Se face clasic prin apăsarea pictogramei corespunzătoare aplicației din meniul principal al telefonului.
/
Fig. 67 : Pictograma aplicației
Primul ecran care se va deschide este ecranul principal și vor fi disponibile fragmentele Home, care va permite căutarea de oferte și Profile care va avea un buton de autentificare. Dacă se încearcă rezervarea utilizatorul va fi redirecționat către ecranul de autentificare și se va afișa un mesaj.
/
Fig. 68 : Încercarea de rezervare fără a fi autentificat

După autentificare devin disponibile fragmentele Trips, Inbox, iar în fragmentul Profile va apărea un meniu cu mai multe opțiuni.
4.3. Autentificare și înregistrare
Pentru accesul la toate funcționalitățile aplicației se face autentificarea introducând email-ul și parola cu care s-a creat contul. În cazul în care utilizatorul nu are un cont se poate înregistra foarte simplu : din ecranul de autentificare se apasă opțiunea din josul ecranului “No account? Register here!” care duce către ecranul de înregistrare.
După completarea datelor cerute în ecranul de înregistrare utilizatorul trebuie să intre pe adresa de email introdusă și să aștepte un email de confirmare a contului din partea aplicației – acest lucru este anunțat printr-un mesaj similar celui care informează utilizatorul că este necesar să fie autentificat pentru rezervare.
Mesajele apar în partea de jos a ecranului și sunt acolo pentru câteva secunde, ele poartă denumirea de Toast Messages.
/
Fig. 69 : Mail de confirmare a adresei

/
Fig. 70 : Mesaj de confirmare email

4.4. Adăugare date în profil
4.4.1. Adăugare poză
Există o poză de profil generică care se poate schimba din ecranul Profile prin simpla apăsare a imaginii și selectarea uneia noi din galeria de imagini a telefonului.

4.4.2. Adăugare servicii
În meniu există trei servicii : tur ghidat, cazare sau loc de parcare și un utilizator poate adăuga oricâte servicii o dată ce are un cont valid. Pentru acest lucru trebuie să selecteze din meniu opțiunea de listare a tuturor serviciilor de un anume tip oferite de către el. În acel ecran va putea de asemenea să adauge un serviciu nou de același tip prin apăsarea butonului din partea dreapta-jos a ecranului.
/
Fig. 71 : Butonul pentru adăugare tururi

Apăsarea butonului ne va duce la primul ecran pentru adăugarea unui nou serviciu. Pentru fiecare serviciu ecranele sunt diferite și în număr diferit, în funcție de complexitate.
/ /
Fig.72 : Primul ecran la adăugare tur Fig. 73 : Primul ecran la adăugare parcări
4.4.3. Ștergere sau ieșire din cont
Tot în cadrul meniului amintit mai sus există opțiunile de Sign Out (ieșire din cont) și Delete account – ștergere cont după confirmarea acestui lucru.
/
Fig.74 : Ecranul de profil cu meniul aferent

4.5. Căutare și filtrare
Din ecranul principal, în fragmentul Home sunt afișate opțiunile de căutare servicii sau vizualizare statistici. La selectarea unui serviciu de căutare ne vor fi afișate o parte dintre serviciile disponibile spre achiziție. Se recomandă folosirea filtrului din partea dreaptă a barei de navigare pentru obținerea unor rezultate mai exacte.
/ /
Fig.75 : Înainte de filtrare Fig. 76 : Datele de filtrare

/
Fig. 77 : După filtrare

4.6. Vizualizarea serviciului în detaliu
Pentru a vedea toate detaliile relevante ale unui serviciu se apasă oriunde în interiorul unei celule din listă.
/
Fig. 78 : O parte dintre detaliile unei cazări

4.7. Achiziționarea unui serviciu
În partea de jos a ecranului cu detalii se află butonul de rezervare, dar în prealabil trebuie selectate date pentru a se verifica disponibilitatea. Acest lucru se face din activitatea de filtrare. Ultimul lucru de făcut înainte de finalizarea unei rezervări este introducerea detaliilor legate de plată.

/
Fig. 79 : Ecranul de plată
4.8. Starea unei rezervări
Pentru a verifica starea unei rezervări există ecranul Trips unde este afișată aceasta, care poate fi “Pending” – în așteptare, “Denied” – respinsă, ”Accepted” – acceptată ,”Incoming” – stare disponibilă doar pentru serviciile oferite de către utilizatorul curent care au fost acceptate și urmează să vină. De asemenea, pe acest ecran sunt trecute și alte date relevante cum ar fi titlul serviciului, perioada de timp selectată, prețul plătit sau suma primită și numărul de telefon.
/
Fig.80 : Ecranul Trips

Pe ecranul Inbox se administrează cererile de rezervare pentru serviciile utilizatorului. Acesta are opțiunile acceptare și respingere rezervare, iar celelalte detalii sunt identice cu cele de pe ecranul Trips.

/
Fig.81 : Rezervare în așteptare

6. CONCLUZII
Dezvoltarea acestei aplicații s-a dovedit a fi foarte complexă, pe parcursul lucrului la ea întâmpinând mai multe obstacole, atât tehnice, cât și din punct de vedere al organizării datelor. Un software de asemenea dimensiuni are multe funcționalități diferite care pot determina potențialii clienți să folosească aplicația.
Faptul că un utilizator poate fi client și proprietar cu un singur cont reprezintă un avantaj din punct de vedere al UX (user experience), tranziția între cele două roluri fiind practic insesizabilă.
Ideea inițială a aplicației a fost de a combina serviciile de cazare cu cele de tur ghidat, dar pe parcurs, după mai multe discuții despre aplicație cu diverse persoane, am hotărât că un serviciu de parcare ar fi bine-venit. După alte discuții am realizat faptul că acest serviciu de parcare ar putea reprezenta defapt rampa de lansare a aplicației într-o piață foarte dezvoltată și competitivă cum este cea a turismului.
În final am realizat o soluție completă pentru vacanțe pe cont propriu oferind servicii de cazare, parcare, ghid turistic și administrator de rezervări. În continuare se vor dezvolta alte servicii auxiliare cum ar fi închiriere de mașini și biciclete sau rezervări la diferite restaurante.

7. BIBLIOGRAFIE
[1] – https://www.e-unwto.org/doi/pdf/10.18111/9789284419876
[2] – https://hackernoon.com/top-3-most-popular-programming-languages-in-2018-and-their- annual-salaries-51b4a7354e06
[3] – https://medium.com/google-developers/instant-run-how-does-it-work-294a1633367f
[4] – https://developer.android.com/guide/components/activities/activity-lifecycle
[5] – https://developer.android.com/about/dashboards
[6] – https://developer.android.com/guide/components/activities/intro-activities
[7] – https://developer.android.com/guide/navigation/navigation-principles
[8] – https://developer.android.com/guide/components/intents-filters
[9] – https://developer.android.com/
[10] – https://firebase.google.com/docs/auth/android/manage-users
[11] – https://firebase.google.com/docs/database/android/read-and-write
[12] – Neil Smyth, Android Studio 3.0 Development Essentials, 2017
[13] – https://firebase.google.com/docs/database/admin/retrieve-data
[14] – https://developers.google.com/maps/documentation/android-sdk/map
[15] – https://developer.android.com/reference/android/location/Geocoder
[16] – Ian Darwin, Android Cookbook, 2017
[17] – Gerardus Blokdyk, Firebase the ultimate step-by-step guide, 2018

Similar Posts