oriol mèlich pons aplicació android appxi treball de fi de...
TRANSCRIPT
Oriol Mèlich Pons
Aplicació Android Appxi
TREBALL DE FI DE GRAU
dirigit per David Girbau Sala
Grau en Enginyeria de serveis i sistemes de telecomunicacions
Tarragona
2018
1
Índex
1. Introducció ................................................................................................................................ 2
1.1. App Taxi .............................................................................................................................. 2
1.2. Competències ..................................................................................................................... 2
1.3. Objectius ............................................................................................................................ 3
1.4. Organització memòria ........................................................................................................ 3
2. Disseny de l’APP ........................................................................................................................ 4
2.1. Esquema de l’aplicació ....................................................................................................... 4
2.2. Desenvolupament aplicació ............................................................................................... 5
2.3. Configuració ....................................................................................................................... 6
2.3.1. Manifest ...................................................................................................................... 6
2.3.2. Gradle .......................................................................................................................... 8
2.4. Classes .............................................................................................................................. 10
2.4.1. Conceptes bàsics de l’entorn Android....................................................................... 11
2.4.2. Java class ................................................................................................................... 12
2.4.3. Activitys ..................................................................................................................... 14
2.5. Permisos ........................................................................................................................... 38
2.6. Diagrames de relacions .................................................................................................... 39
2.7. Diagrama de seqüència .................................................................................................... 41
3. BBDD........................................................................................................................................ 42
3.1. Organització ..................................................................................................................... 42
3.1.1. Pla Firebas ................................................................................................................. 42
3.1.2. Utilitats que ens proporciona Firebase ..................................................................... 44
3.1.3. Sincronització firebase-aplicació ............................................................................... 49
3.2. Comunicació ..................................................................................................................... 50
4. Resultats .................................................................................................................................. 52
5. Conclusions i línies futures ...................................................................................................... 56
5.1. Conclusions ...................................................................................................................... 56
5.1.1 Conclusió personal ..................................................................................................... 56
5.1.2 Retrospectiva dels objectius plantejats i assolits. ...................................................... 57
5.2. Línies futures .................................................................................................................... 57
6. Referències .............................................................................................................................. 58
2
1. Introducció
1.1. App Taxi
Tot comença amb la necessitat persónal en un viatge on havíem d’utilitzar el taxi com a mitjà de
transport molt recurrent, i comunicar-nos parlant no era una opció. En aquest punt vaig veure
un buit molt gran en un sistema que se l’acusa d’estar molt obsolet. El mon del taxi funciona
d’una manera molt arcaica i cal adaptar-lo a les noves tecnologies.
Mirant l’oferta que tenim al mercat de les aplicacions relacionades amb el taxi, veiem una gran
varietat de programes, la més important d’elles és Mytaxi[1], però la majoria es queden a la
superfície, es limiten a fer una trucada a la centraleta de taxis, t’ubiquen i t’envien un vehicle.
El que fan és simplement estalviar-te una trucada telefònica.
En aquesta aplicació, pretenc anar més enllà.
Vull dissenyar una aplicació que beneficiï tant als usuaris dels taxi, com als propis taxistes. Als
clients, donant-los facilitats per trobar el seu mitja de transport de manera fàcil i eficaç, amb
una comunicació directe en tot moment amb el taxi sol·licitat i un càlcul de taxes, en funció del
temps i la distancia a recórrer; i als taxistes facilitant-los una eina que els faci transmetre
confiança als seus clients, i al mateix temps els aconsegueixi el màxim número de carreres.
1.2. Competències
Per dur a terme aquest projecte calen nocions de com estructurar els objectes i les classes Java
i tenir coneixements bàsics d’extensible markup lenguage (XML), per a la nostra interfície gràfica
i poder-ho relacionar amb la part Java.
També necessito saber com funciona una base de dades no relacional, ja que utilitzaré
Firebase[2], i d’aquesta aplicació que ens ofereix Google, a part de la base de dades a temps
real, que és part del nucli de l’ aplicació, també utilitzaré Firebase Authenticator. Per tant caldrà
saber moure’s en l’entorn de les interfícies de programació d’aplicacions (APIs) i utilitzar els kits
de desenvolupament de software (SDKs).
Finalment hauré de conèixer profundament com funciona i com està estructurat l’Android
Studio[3], ja que els entorns de desenvolupament integrat (IDE) ajuden i faciliten molt les coses
a l’hora de desenvolupar i simular, i també hauré de saber com gestionar la part tant del projecte
en si, com del manifest i els gradles.
3
1.3. Objectius
Dissenyar i crear una aplicació d’Android funcional, amb una comunicació directa i a temps real
amb una BBDD, que permeti un contacte entre el taxista i el client, i que serveixi per acordar i
gestionar el servei.
Per aconseguir aquest objectiu, hauré d’aprofundir amb els coneixements que tinc, tant de Java,
XML, Android Studio; treballar a nivell de APIs i amb una de les meves eines principals que serà
Firebase, que és la base de dades gratuïta (amb la seva versió de prova utilitzada per
desenvolupar programari) ofert per Google.
Tots aquets sistemes són de codi obert i programari lliure, per tant el cost de l’ aplicació no és
més que el temps invertit.
1.4. Organització memòria
L’Organització d’aquesta memòria pretén mostrar primer el producte, per donar una idea clara
d’on vull arribar, i quines etapes i quins mètodes he utilitzat per aconseguir-ho.
Un cop clara l’aplicació, indagaré de dalt cap a baix tant amb els passos que he seguit com amb
el contingut tècnic de la pròpia aplicació, codi desglossat, integració de APIs, etc.
Més endavant explicaré com integrar la base de dades de Firebase al meu projecte, com
l’estructuro a nivell de referències i com es realitza la comunicació a temps real.
Finalment exposaré les conclusions i línies futures.
4
2. Disseny de l’APP
2.1. Esquema de l’aplicació A la figura 0 es mostra un primer contacte amb la navegació per a la nostra aplicació.
Com es pot veure, a grans trets, l’aplicació consta d’una pantalla inicial per seleccionar quin tipus
d’usuari ets (client o taxista). Un cop seleccionat el tipus d’entrada, hi haurà una pantalla de
login o registre i, un cop dins, es veurà, si s’hi ha entrat com a client, un mapa que indicarà la
nostra posició i la posició relativa dels taxis; a la part inferior de la pantalla hi haurà dos botóns,
un per iniciar la geo-localització i l’altre per sol·licitar el taxista seleccionat.
Per alta banda, si s’entra com a taxista, tindrem un botó per iniciar la geo-localització i l’altre per
acceptar o rebutjar el taxista.
Figura0: Sumari de l’aplicació.
5
2.2. Desenvolupament aplicació
En aquest apartat s’explica l’organització seguida per desenvolupar l’aplicació.
Aquesta idea va sorgir, com s’ha dit en el primer apartat, d’una necessitat. La vam desenvolupar
com a projecte conceptual en una assignatura de quart (Projectes de Telecomunicacions) i en
aquest TFG és on s’ha implementat. Allò em va donar la idea de treballar en petits objectius, que
és com he acabat funcionant.
El primer pas ha estat assignar uns objectius, que tal com veurem al final, la majoria s’han
acomplert. Posteriorment s’han definit una sèrie de tasques per implementar-los. Cada cop que
una tasca donava un problema inesperat, s’afegien noves sub-tasques per acabar la principal.
Per exemple, quan es pensava que agafar la posició al mapa i pintar-la al Google maps seria
simplement crear una activity “maps” i cridar a myLocation, es van haver de fer diferents sub-
tasques de com integrar el maps i el gps per separat, i també com sol·licitar els permisos que
necessitava.
Des d’un principi es tenia clar que la primera versió de l’aplicació no seria la que es mostra a
l’usuari final; per tant es va comptar en fer la versió de proves que ajudaria a testejar l’aplicació
i, després, en desfer tot el sobrant perquè l’usuari tingués la seva pròpia versió.
Finalment es va plantejar una manera de fer les proves de la meva aplicació amb usuaris reals
però amb el codi una mica modificat, de manera que els usuaris només poguessin ser taxis i jo
pogués interactuar amb ells.
6
2.3. Configuració
Abans de començar a crear classes i activitys, s’ha de posar a punt la configuració del manifest i
el gradle, on es donaran unes instruccions a l’aplicació, i servirà per sincronitzar les APIs.
2.3.1. Manifest
El manifest és la configuració principal de l’aplicació, aquí es configura quina activity serà la
principal a l’hora d’executar-se la app, es declaren les diferents activitys que es tindran, es dona
nom a la app a la barra de navegació, etc.
Al manifest hi ha un conjunt d’informació molt rellevant per a l’aplicació així com els permisos
indispensables que es necessiten del dispositiu per executar-la. En aquest cas particular,
necessita accés a internet i a la posició del dispositiu.
La posició es pot determinar de vàries maneres. Per tant no només necessito accedir al GPS,
sinó que també cal accedir al paquet principal d’opcions que ens ofereixen posicionament al
mapa, WIFIs, etc. També serveix per sincronitzar APIs com la de Google maps.
Google t’ofereix una sola API key gratuïta[4] per desenvolupar la teva aplicació, aquesta s’ha
d’introduir en el manifest com a meta-data.
El manifest també determina la ruta de workspace utilitzada i el Package que contindrà les
classes.
En el codi 1 podem veure la configuració del nostre manifest on cal ressaltar la importància dels
<uses-permission> que ens permetran declarar els permisos necessaris de l’aplicació i
<activity> que ens diran les activitys que figuren en aquesta.
<?xml version=”1.0” encoding=”utf-8”?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
6efiní6=”urv.apptfg”>
<!—This is required for Picasso to work. →
<uses-permission android:name=”android.permission.INTERNET” />
<!—Used to adjust the work load depending on the type of network
the 6efiní is using. →
<uses-permission
android:name=”android.permission.ACCESS_NETWORK_STATE” />
<!—Used to load images from the gallery content provider. →
<uses-permission
android:name=”android.permission.READ_EXTERNAL_STORAGE” />
<uses-permission
android:name=”android.permission.ACCESS_COARSE_LOCATION” />
<uses-permission
android:name=”android.permission.ACCESS_FINE_LOCATION” />
<uses-feature android:name=”android.hardware.location.gps” />
7
<application
android:allowBackup=”true”
android:icon=”@mipmap/ic_launcher”
android:label=”@string/app_name”
android:supportsRtl=”true”
android:theme=”@style/AppTheme”>
<activity android:name=”.Inici”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category
android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
<activity android:name=”.MapsActivity” />
<activity android:name=”.iniciTaxista” />
<activity android:name=”.iniciClient” />
<activity android:name=”.registreTaxista” />
<activity android:name=”.registreClient” />
<!--
The API key for Google Maps-based APIs is defined as a
string resource.
(See the file “res/7efiní/google_maps_api.xml”).
Note that the API key is linked to the encryption key
used to sign the APK.
You need a 7efiní7t API key for each encryption key,
including the release key that is used to
sign the APK for publishing.
You can 7efiní the keys for the debug and release targets
in src/debug/ and src/release/.
→
<meta-data
android:name=”com.google.android.gms.version”
android:value=”@integer/google_play_services_version” />
<meta-data
android:name=”com.google.android.geo.API_KEY”
android:value=”AizaSyA3ROFLejw2mvokz9VylK-zAyQ1kOT2vQI” />
<activity
android:name=”.Client”
android:label=”@string/title_activity_client” />
<activity android:name=”.ProvaObjectes”></activity>
</application>
</manifest>
Codi1: configuració del manifest
Cal recordar també que Android és un sistema operatiu en constant evolució i per tant hi ha una
cosa que cal remarcar. Abans els permisos es concedien a la pantalla d’inici de l’aplicació i tots
alhora. Actualment els permisos es demanen quan es fan servir.
Un exemple d’aquest sistema seria que, en entrar a l’aplicació, només et demanaria els permisos
del GPS quan iniciessis la geo-localització. Més endavant es mostrarà com funciona el sistema
de sol·licitud de permisos en execució.
8
2.3.2. Gradle
El gradle és l’encarregat de configurar les versions de l’aplicació. Tothom sap que tant Android
com les APIs que utilitzem evolucionen amb el temps, i s’ha d’anar en compte en què totes les
versions tant de l’Android com de les APIs utilitzades siguin compatibles.
Al codi 2, podem veure la versió d’Android i de google-services. Aquest gradle es genera
automàticament després de fer el projecte i escollir la versió d’Android.
// Top-level build file where you can add configuration options common
to all sub-projects/modules.
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
classpath 'com.google.gms:google-services:3.0.0' // google-
services plugin
// NOTE: Do not place your application dependencies here; they
belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Codi2: Configuració gradle
Després tenim el gradle on configuraré la versió de SDK i APIs, que es mostra a Codi 3.
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion '27.0.3'
defaultConfig {
applicationId "urv.apptfg"
minSdkVersion 23
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
9
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-
android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation 'com.android.support.constraint:constraint-
layout:1.1.2'
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-
core:2.2.2', {
exclude group: 'com.android.support', module: 'support-
annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
testCompile 'junit:junit:4.12'
compile 'com.google.firebase:firebase-core:9.0.0'
compile 'com.google.firebase:firebase-database:9.0.0'
compile 'com.google.firebase:firebase-auth:9.0.0'
implementation 'com.google.android.gms:play-services-maps:9.0.0'
compile 'com.google.android.gms:play-services-maps:9.0.0'
implementation 'com.android.support:recyclerview-v7:25.4.0'
}
apply plugin: 'com.google.gms.google-services'
Codi3: Versions gradle
En aquest cas particular s’ha hagut de detallar la versió de Google maps que s’utilitza, de declarar
la funcionalitat del recycler view i també s’han hagut d’afegir els diferents serveis de Firebase:
database i l’authenticator. Aquest últim és el servei d’autentificació que veurem quan detalli les
activitys.
Com es veu al codi 3, totes les versions són les 9.0.0, i no perquè siguin les més actuals, sinó
perquè com que tot ha de ser compatible entre si, no es pot fer servir una versió més moderna
d’una cosa que de l’altra; i en aquesta versió hi ha compatibilitat.
El principal problema d’aquest sistema és que tant l’Android com les diferents APIs evolucionen
a diferent ritme, i s’ha d’anar ajustant contínuament la versió. Això també implica canvis
substancials a la manera d’accedir a les pròpies APIs.
10
2.4. Classes
El projecte està distribuït en tres Java class i nou activitys.
Les activitys són el conjunt d’una Java class més una classe XML per donar-li forma a la interfície,
i se’n necessita una per a cada pantalla que tingui l’aplicació: inici, pantalla registre, etc.
Algunes d’aquestes activity seran invisibles a l’usuari final, però són molt útils a l’hora de
desenvolupar el codi. Per tant, en aquest apartat es comentarà totes les classes utilitzades i es
mostraran les activitys com es veuen a l’hora del desenvolupament.
En l’apartat de resultats finals ja es mostrarà com queden aquestes interfícies per l’usuari últim.
A la figura 1 podem veure l’estructura d’activitys del projecte.
Figura1: Estructura Activitys
11
2.4.1. Conceptes bàsics de l’entorn Android
Abans de començar a comentar les classes una per una, es farà una explicació de dos conceptes
que es repetiran en totes les activitys i cal tenir clar què són i perquè serveixen:
Intent:
Intent és un mètode abstracte de l’entorn Android que permet fer crides a les altres activitys.
Context
El context és una interfície abstracta, que crea l’entorn per poder accedir a tot el “context” de
l’aplicació, que és l’estructura de nivell superior en una app.
Una app Android crea automàticament un context, i tots els mètodes Android cridats per
defecte amb l’Android Studio s’executen en el marc d’aquest context.
Aquest s’encarrega de llançar noves Activitys, escoltar els intents i emetre’ls en brodcast perquè
totes les Activitys sàpiguen que hi ha un missatge de crida.
Toast
Són els missatges emergents que duren uns segons en pantalla, tal i com veiem a la figura 2:
Figura2: Mostra toast
Aquets missatges són molt útils a l’hora de donar informació durant l’execució del programa,
com quan et registres correctament o et desconnectes de l’aplicació.
12
A part de la utilitat de donar informació a l’usuari final, també és molt útil en l’entorn de
desenvolupament ja que Android Studio incorpora una consola per seguir tot el programa, però
no inclou un mètode per depurar el codi (debugger). Per tant, la millor manera per anar sabent
quines variables hi ha en cada moment, es anar deixant toast durant el funcionament del
programa. A la figura3 tenim un exemple de l’esmentat anteriorment.
Figura3: Mostra informació toast
2.4.2. Java class
Tot seguit comentaré les classes, una per una, seguint l’ordre definit anteriorment.
Hi ha tres Java class al projecte. Dos són objectes i l’altra relaciona les referències del projecte
amb els de la base de dades de Firebase.
Clients
Cada cop que es registra un client a l’activity Clients, s’hauran de passar els seus atributs per
poder-lo crear com a objecte. El seu constructor es pot observar al codi4:
public Clients() {
}
public Clients(String nom, String mobil, String email,String pos) {
this.nom = nom;
this.email = email;
this.mobil = mobil;
this.pos=pos;
}
Codi4: Constructor Clients
Sempre es deixa un constructor buit, perquè s’ha de tenir en compte que és un projecte que no
està finalitzat, i sempre es poden fer canvis a l’hora de crear un objecte. Així forcem que es creï
segons convingui.
13
Un cop registrat un client, es té l’objecte a la base de dades completament funcional gràcies a
la funció de registrar. La classe Clients només dona forma a l’objecte i ens proporciona els getters
i els setters. Els getters i setters són mètodes pre-definits de java i auto-generats per l’IDE per
consultar i modificar les variables.
Taxistes
Al igual que Clients, Taxistes és una Java class que només dona forma a l’objecte, i ens
proporciona els getters i setters. Mostrem el seu constructor en el codi 4.
public Taxistes() {
}
public Taxistes(String nom, String codi, String mobil, String
usuari) {
this.nom = nom;
this.codi = codi;
this.mobil = mobil;
this.usuari = usuari;
this.ocupat=false;
}
Codi4: Constructor Taxistes
FirebaseReference
Aquesta classe proporciona les relacions entre les referències de l’app i les de la Firebase. Segons
com s’estructurin les referències, es tindrà un sistema basat en arbres en el qual s’hi pot accedir
navegant pels childs, que vindrien a ser les seves branques. Funciona similar a un sistema de
carpetes on el node final és l’objecte.
En el codi 5, es poden observar les diferents referències de l’aplicació. Les referències[5]
utilitzades són Taxistes i Clients, que estructuren la base de dades diferenciant aquestes dues
entitats. En un principi s’estructuraven aquests dos childs en un de més gran que era Usuaris,
utilitzant herència per fer una distinció entre els que eren taxistes i els que eren clients. Però
això sumava molta complexitat a l’hora de cridar les referències a l’aplicació, i tampoc no era
massa útil a l’hora d’estalviar feina de desenvolupament.
public class FirebaseReference {
//final public static String USUARIS_REFERENCE= "Usuaris";
final public static String TAXISTA_REFERENCE= "Taxistes";
final public static String CLIENT_REFERENCE= "Clients";
}
Codi5: Referencies firebase.
14
2.4.3. Activitys
En aquest apartat es comentaran les activitys, ajuntant la seva part de “processat”, que seria la
Java class, més la seva part de interfície que es l’XML. S’ha de tenir en compte que aquestes
parts estan altament relacionades i es poden fer funcions a la Java class que canviïn l’XML.
Inici
A la figura 4 es mostra l’Activity Inici en la seva versió de prova. La funció onCreate serveix per
executar totes les instruccions necessàries a l’hora d’obrir l’activity.
Figura4: Activity Inici Desenvolupament
Per associar la classe Java amb la interfície XML cal utilitzar la instrucció setContentView tal i
com veiem en el codi 6. I el mètode bind() tan sols és una manera d’estructurar la classe que
ensenyarem a continuació.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inici);
bind();
}
Codi6: onCreate Inici
En aquesta activity tenim el logotip de l’aplicació i tres botóns (dos en la versió final). Aquests
botóns tenen l’única funció de redirigir l’usuari a les activitys principals de taxista o clients
mitjançant un intent a IniciTaxista o IniciCleints. L’estructura dels intents quasi sempre tindrà el
mateix format: es declara un botó, es posa “clickable”, i s’assigna la funció que es vol que realitzi
en el cas que es premi, dins el mètode onClick. Dins aquest mètode, es crea un intent per accedir
a la següent activity i es sol·licita el context de l’activity de destí, tal i com es veu al codi 7.
15
private void bind(){
taxiButton = (Button) this.findViewById(R.id.taxistabutton);
taxiButton.setClickable(true);
taxiButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setClass(getApplicationContext(),
iniciTaxista.class);
startActivity(intent);
}
});
clientButton = (Button) this.findViewById(R.id.clientbutton);
clientButton.setClickable(true);
clientButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setClass(getApplicationContext(),
iniciClient.class);
startActivity(intent);
}
});
provaButton = (Button) this.findViewById(R.id.provabutton);
provaButton.setClickable(true);
provaButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setClass(getApplicationContext(),
ProvaObjectes.class);
startActivity(intent);
}
});
}
Codi7: Metode bind()
Un cop polsat el botó, el context llença el missatge de brodcast i la classe referida executa el seu
mètode onCreate.
El botó de proves fa un intent a la classe ProvaObjectes. Aquesta classe és interesant perquè és
una mescla de tots els recursos utilitzats en l’aplicació.
Com he dit anteriorment, el mètode bind() serveix únicament per seguir una metodologia de
treball, ja que l’Android Studio t’ofereix un seguit d’eines per saber en qualsevol moment dins a
quins mètodes estàs i dins de quines classes, tal i com es veu en la figura 5.
Figura 5: Navegació Android Studio.
16
Seguir un criteri per disposar tota l’estructura de paquets de classes i mètodes és molt
important a l’hora de re-escriure contingut i seguir el programa, ja que no es disposa d’un
mètode debugger com a tal. Només es disposa d’una consola, i al ser un entorn de creació amb
moltes funcions al background, interessa tenir l’estructura el màxim clara possible, ja que l’únic
sistema per seguir les dades és amb la utilització de toasts; en el codi 8 podem veure l’estructura
d’un toast. Els toasts són els missatges emergents que surten durant uns segons en fer una acció,
tal i com veiem a la figura 6.
Context context = getApplicationContext();
Toast.makeText(context, mail + "tu KEY" + taxiKey,
Toast.LENGTH_LONG).show();
Codi8: estructura toast.
Figura6: Toast emergent pantalla prova.
Aquí es mostra un exemple de toast, utilitzat per comprovar que s’agafava la clau correcta de la
base de dades de Firebase juntament amb el correu de la sessió que s’havia iniciat.
Referent a la classe XML que acompanya a la classe Java Inici, no té massa rellevància. Es tracta
d’un Linear Layout que permet posar la imatge i estructura els botóns d’una forma vertical, cada
element per sota de l’anterior.
Cada element té la seva pròpia configuració: altitud, amplada, color, gravetat. Però en aquest
cas, estan tots amb el mateix disseny. Així que tan sols es comentarà els XML amb coses més
remarcables, com els mapes o les activitys de prova.
17
ProvaObjectes
El tercer botó de l’inici és el més important i interesant a nivell tècnic. Per tant es considera
oportú explicar aquesta activity en segon lloc, ja que totes les següents tenen alguna relació amb
el funcionament d’aquesta, tot i que l’usuari final no la podrà utilitzar, perquè té un
funcionament únic i exclusiu pel desenvolupament i per fer jocs de proves de manera ràpida.
Servirà tant per provar la comunicació amb la Firebase amb l’ajuda d’un recycle view[6], com
per ajuntar totes les utilitats de l’aplicació en una sola activity de proves. A la figura 7 en podem
veure el resultat.
Figura7: Activity prova
A continuació es desglossaran els diferents elements exposats.
recyclerView
Aquest element és relativament recent en Android i permet crear llistes en les quals només es
carreguen els elements que tenim en pantalla. La principal diferència respecte a una llista
normal, és que si una llista té dos-cents elements, aquesta carregaria tots els elements, fent que
aquest procés tingués molta càrrega per al dispositiu. En canvi amb un recycleView, només
carrega els elements que pot visualitzar. Així que si es té una llista de tres mil elements, però
només en calen visualitzar deu, el dispositiu només haurà de carregar aquests deu, i a l’hora de
navegar per la llista, es van carregant els elements un a un.
18
Aquest tipus de llista s’acompanya d’una classe anomenada adapter, que és l’encarregada de
donar-li format i informació a cada cel·la; el procés de proporcionar informació s’anomena
inflate _aquest concepte s’utilitzarà més endavant. Aquestes classes estan estretament
relacionades, ja que una és la posició i l’altra el dipòsit.
Per tant, la classe Adapter s’estendrà d’una classe RecycleView tal i com es veu al codi 9:
public class Adapter extends
RecyclerView.Adapter<Adapter.TaxistesViewHolder>{
List<Taxistes> taxistes;
public Adapter(List<Taxistes> taxistes) {
this.taxistes = taxistes;
}
@Override
public TaxistesViewHolder onCreateViewHolder(@NonNull ViewGroup
parent, int viewType) {
View v =
LayoutInflater.from(parent.getContext()).inflate(R.layout.row_recyvler
,parent, false);
TaxistesViewHolder holder = new TaxistesViewHolder(v);
return holder;
}
@Override
public void onBindViewHolder(@NonNull TaxistesViewHolder holder,
int position) {
Taxistes taxista = taxistes.get(position);
holder.nom.setText(taxista.getNom());
holder.gps.setText(taxista.getLatitud()+"
"+taxista.getLongitud());
}
@Override
public int getItemCount() {
return taxistes.size();
}
public static class TaxistesViewHolder extends
RecyclerView.ViewHolder {
TextView gps,nom;
public TaxistesViewHolder(View itemView) {
super(itemView);
gps = (TextView) itemView.findViewById(R.id.textViewgps);
nom = (TextView) itemView.findViewById(R.id.textViewNom);
}
}
}
Codi9: Estructura Adaptador Recycler View
19
A la figura 8 es veu el layout resultant de l’ activity de proves.
Figura8: Estructura interfície de l’activity proves.
La part més important d’aquest codi és la de l’inflate, que és un mètode abstracte que serveix
per assignar tots els valors sol·licitats des de la Firebase a l’adapter, de tal manera que es podrà
omplir la llista amb la informació real que rebi des de la base de dades. Aquest procés s’anomena
“inflar” el context.
Aquesta classe anirà lligada tant amb la interfície de la fila, que li direm row_recyvler, com amb
el recycleView que implementarem a la classe ProvaObjectes. La classe XML row_recyvler
assignarà una interfície determinada a la fila que ompli a la llista, tal i com es veu en el codi 10.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/textViewgps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="gps"
android:textAlignment="center"
android:textAppearance="@android:style/TextAppearance.Material.Large"
/>
<TextView
20
android:id="@+id/textViewNom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="nom"
android:textAlignment="center"
android:textAppearance="@android:style/TextAppearance.Material.Large"
/>
<View
android:layout_width="wrap_content"
android:layout_height="1px"
android:background="@android:color/darker_gray" />
</LinearLayout>
Codi10: XML adapter
Aquest Codi 10 reflecteix l’estructura que té la interfície de la figura 9:
Figura9: Estructura adaptar.
En aquest punt ja es pot omplir el RecycleView amb els adapters creats anteriorment. Dins el
LinearLayout que contindrà tant la llista com el mapa, s’introdueix la part del recycle view, on se
li dona forma al codi 11.
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
Codi11: XML RecyclerView.
21
Realment, aquest es tracta com si fos un layout normal en la part del XML. La part complexa ve
donada a la classe Java, on per omplir de contingut cada cel·la, s’haurà de recórrer a la base de
dades de Firebase.
Al codi 12 es creen els objectes necessaris per a l’activity. Per tant en aquest punt es tindran
creats un Adaptar, un RecicleView i una Llista de taxistes, que són el tipus d’objecte amb els
quals inflarem l’adapter.
public class ProvaObjectes extends FragmentActivity {
RecyclerView rv;
List<Taxistes> taxistes;
Adapter adapter;
Codi12: Declarar objectes a ProvaObjectes.
Dins el mètode onCreate que tenim al codi 13, hi ha la part de super.onCreate que serveix per
heretar tot el context que teníem anteriorment, i la part de setContentView que ens assignarà
la interfície a la activity.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_prova_objectes);
Codi13: OnCreate provaObjectes.
rv = (RecyclerView) findViewById(R.id.recyclerView);
rv.setLayoutManager(new LinearLayoutManager(this));
taxistes = new ArrayList<>();
FirebaseDatabase database = FirebaseDatabase.getInstance();
adapter = new Adapter(taxistes);
rv.setAdapter(adapter);
Codi14: Inflate del Recycler View
En el codi 14 es relacionen tots els elements; primer es dona valor al array de taxistes, es posa
cada element en un adapter i es col·loquen dins el recyclerView. S’assigna el recyclerView que
s’ha creat al XML, es crea la llista de taxistes per poder omplir els adapters, es crea un adaptar
de taxistes i aquest s’introdueix dins el recyclerView. Cal recordar que la base de dades està
dividida entre taxistes i clients, tal i com es mostra a la figura 10.
Figura10: Jerarquia Firebase.
22
Per tant, per capturar la informació de cada node de la base de dades, primer s’ha hagut d’entrar
a la carpeta taxistes, tal i com definim al codi 15, i així es podrà navegar per tots els nodes que
existeixen dins aquesta referència tal i com veiem a la figura 11.
database.getReference("Taxistes").addValueEventListener(new
ValueEventListener() {}
Codi15: Definició path referència taxista.
Figura11: Nodes Taxistes.
Per utilitzar l’eina que proporciona la lectura d’informació a temps real, es necessita utilitzar el
mètode onDataChange el qual es mostra al codi 16, que el proporciona l’API de Firebase. Aquest
mètode s’executarà cada cop que hi hagi un canvi a dins la carpeta referència[7].
database.getReference("Taxistes").addValueEventListener(new
ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if( myMarker != null){
mMap.clear();
}
taxistes.removeAll(taxistes);
for (DataSnapshot snapshot :
dataSnapshot.getChildren()) {
Taxistes taxista = snapshot.getValue(Taxistes.class);
taxistes.add(taxista);
lon = taxista.getLongitud();
lati = taxista.getLatitud();
nom= taxista.getNom();
mobil = taxista.getMobil();
ocupat= taxista.isOcupat();
}
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
Codi 16: Mètode onDataChange.
23
Dins al mètode onDataChange, es fa un foreach, que permet recórrer tota la llista d’objectes
dins la carpeta referència, i capturar la informació que sigui necessària.
Tornant a l’adapter, per crear una fila cal utilitzar el mètode del codi 17.
@Override
public void onBindViewHolder(@NonNull TaxistesViewHolder holder, int
position) {
Taxistes taxista = taxistes.get(position);
holder.nom.setText(taxista.getNom());
holder.gps.setText(taxista.getLatitud()+"
"+taxista.getLongitud());
}
Codi17: Mètode onBindViewHolder.
On es creen les variables locals “nom” i “gps”, tal i com es veu al codi 18, es fan els gets dels
valors del taxista i s’assignen aquest valors al XML i, finalment, es mostren per pantalla.
public static class TaxistesViewHolder extends RecyclerView.ViewHolder
{
TextView gps,nom;
public TaxistesViewHolder(View itemView) {
super(itemView);
gps = (TextView) itemView.findViewById(R.id.textViewgps);
nom = (TextView) itemView.findViewById(R.id.textViewNom);
}
Codi18: Constructor TaxistesViewHolder.
Per tant hi han dues opcions: fer el get directament de l’objecte en qüestió (this), o assignar
valors a unes altres variables en el moment del foreach. En el codi anterior s’utilitza ambdós
versions perquè no només es crea l’adapter, sinó que més endavant es mostrarà la part del mapa
on ja es pinten totes les posicions dels taxistes.
Mapa
La part del mapa té un XML directament relacionat amb l’API de Google tal i com veiem al codi
19.
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="250dp" />
Codi19: Abstracte XML google maps
Aquesta part de codi està proporcionada directament per google, i l’únic que s’ha fet ha sigut
ajustar el layout a la pantalla segons ha sigut necessari.
El que s’ha de tenir en compte per distribuir els markers pel mapa, és agafar els atributs de cada
objecte a mesura que recorri la llista i utilitzar la funció onMapReady() tal i com es veu al codi20,
que assignarà cada taxista al mapa segons les coordenades que tingui.
24
database.getReference("Taxistes").addValueEventListener(new
ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if( myMarker != null){
mMap.clear();
}
taxistes.removeAll(taxistes);
for (DataSnapshot snapshot :
dataSnapshot.getChildren()) {
Taxistes taxista = snapshot.getValue(Taxistes.class);
taxistes.add(taxista);
lon = taxista.getLongitud();
lati = taxista.getLatitud();
nom = taxista.getNom();
mobil = taxista.getMobil();
ocupat= taxista.isOcupat();
onMapReady(mMap);
}
adapter.notifyDataSetChanged();
}
Codi20: Mètode onDataChange.
La funció onMapReady() es mostra al codi 21, i a part de situar el marker al mapa, també donarà
informació extra de l’objecte, com ara el nom o el mòbil del taxista i si està sol·licitat o ocupat...
segons el codi de colors que es tingui ideat.
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
LatLng localitzacioTaxi = new LatLng(lati,lon);
if (lati == 0 && lon == 0 ) {
} else {
if (ocupat!=false) {
myMarker = mMap.addMarker(new
MarkerOptions().position(localitzacioTaxi).title("Nom :"+nom+" Mobil:
"+mobil).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFa
ctory.HUE_RED)));
} else {
myMarker = mMap.addMarker(new
MarkerOptions().position(localitzacioTaxi).title("Nom :"+nom+" Mobil:
"+mobil).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFa
ctory.HUE_GREEN)));
}
}
}
Codi21: Mètode onMapReady
25
Inici Client
Aquesta part de la interfície XML s’explicarà amb l’ajuda que ens proporciona l’Android Studio
com a IDE, tal i com veiem a la figura 12, on es tenen tot un seguit d’opcions i es poden ajustar
a una pantalla que simula el mòbil, com es pot comprovar a la figura 13. Un cop fixats tots els
elements, els ajustos finals es faran en el codi XML, ja que et permet més precisió en alguns
detalls. Però l’estructura general de l’activity es pot fer en l’apartat de disseny amb molta
facilitat.
Figura 12: Estructura XML en forma d’arbre.
Figura 13: Estructura interfície activity iniciClient.
26
En accedir a l’apartat de l’aplicació dirigida als clients, s’entra a la pantalla de la figura 14.
Figura14: Activity IniciClient.
En el codi 22 es veuen tots els botóns i camps de text que s’han de declarar i la funció onCreate
de l’activity
public class iniciClient extends AppCompatActivity implements
View.OnClickListener {
Button iniClientButton;
TextView registClient;
EditText email, contrassenya;
static String accesMail;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inici_client);
iniClientButton = (Button)
this.findViewById(R.id.iniciarClient);
email = (EditText) this.findViewById(R.id.usuariClient);
contrassenya = (EditText)
27
this.findViewById(R.id.passwordClient);
iniClientButton.setClickable(true);
iniClientButton.setOnClickListener(this);
registClient = (TextView)
this.findViewById(R.id.registrarClient);
registClient.setClickable(true);
registClient.setOnClickListener(this);
}
Codi22: Codi per declarar IniciClient
Un cop es tenen els botóns declarats i funcionals (clicables), es farà el mètode onClick tal i com
es mostra al codi 23.
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.registrarClient:
Intent intent = new Intent();
intent.setClass(getApplicationContext(),
registreClient.class);
startActivity(intent);
break;
case R.id.iniciarClient:
if (email.getText().toString().isEmpty() ||
contrassenya.getText().toString().isEmpty()){
Context context = getApplicationContext();
Toast.makeText(context, "Algun camp obligatori no esta
ple!", Toast.LENGTH_LONG).show();
}else {
String mail = email.getText().toString();
String pass = contrassenya.getText().toString();
iniClient(mail, pass);
}break;
}
}
Codi23: Mètode onClick.
En aquest mètode es farà un switch dels dos botóns que hi ha: el d’iniciar sessió o el de registrar.
Si es prem el botó de registrar, el context farà un intent a l’activity de registrarClient. En el cas
que es polsi el botó d’iniciar sessió, aquest farà una comprovació que els dos camps estiguin
emplenats correctament amb la funció isEmpty(); després “castejarà” el contingut a text, per
poder-ho tractar tot com un string. “Castejar” o cast en anglès vol dir canviar el format de
l’objecte; per exemple, si tenim un número amb format de integer i li fem un cast a String, aquest
estarà tractat com si fos un seguit de caràcters. Després s’assignen els valors que he escrit a les
variables mail i pass que es tenen declarats en aquesta classe i es passen al mètode iniClient(mail, pass);
En aquest punt, s’utilitzarà l’API d’autentificació de Firebase que permet comprovar si les
credencials són correctes o no, tal i com es veu al codi 24. Si aquestes són correctes, es podrà
accedir a la següent activity i sortirà un missatge de benvinguda. També permetrà estar
connectats directament amb la base de dades i poder llegir i escriure les dades necessàries per
fer funcionar l’aplicació. Si les credencials no són correctes, apareixerà un missatge d’error del
tipus toast.
28
private void iniClient(String mail, String pass){
FirebaseAuth.getInstance().signInWithEmailAndPassword(mail,pass).addOn
CompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if(task.isSuccessful()){
Intent intent = new Intent();
intent.setClass(getApplicationContext(),
Client.class);
startActivity(intent);
Context context = getApplicationContext();
Toast.makeText(context,
"Benvingut!"+email.getText().toString(), Toast.LENGTH_LONG).show();
accesMail = email.getText().toString();
}else{
Context context = getApplicationContext();
Toast.makeText(context, "Uauari o contrassenya no
vàlida!", Toast.LENGTH_LONG).show();
}
}
});
}
Codi24: Autentificar Client.
Per finalitzar totes les activitys menys la principal, es configurarà el botó de tirar enrere com es
veu en el codi 25 que tenen tots els mòbils Android, de manera que al ser polsat tanqui l’activity
actual i torni a l’anterior:
@Override
public void onBackPressed(){
Intent intent = new Intent();
intent.setClass(getApplicationContext(), Inici.class);
startActivity(intent);
super.onBackPressed();
finish();
}
Codi25: Botó de retrocedir.
RegistreClient
Un cop s’accedeixi a registrar-se com a client, tindrem els atributs del codi 26.
public class registreClient extends AppCompatActivity {
private EditText nom,mobil, email,contrassenya;
private Button registrarClient;
FirebaseDatabase databaseClient = FirebaseDatabase.getInstance();
DatabaseReference clientRef = databaseClient.getReference();
Codi26: Declaració registreClient
29
on s’utilitzarà el formulari de la figura 15 per donar valors a aquests atributs.
Figura15: Formulari registre.
Una vegada omplert, s’assignarà els atributs al node del client mitjançant el codi 27.
Private void registrarClient(String emailr,String contra){
FirebaseAuth.getInstance().createUserWithEmailAndPassword(emailr,contr
a).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
private void registrarClient(String emailr,String contra){
FirebaseAuth.getInstance().createUserWithEmailAndPassword(emailr,contr
a).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if(task.isSuccessful()){
Context context = getApplicationContext();
Toast.makeText(context, “S’ha registrat
29óvil29tamente!”, Toast.LENGTH_LONG).show();
Clients client = new
Clients(nom.getText().toString(),29óvil.getText().toString(),
email.getText().toString(),null);
clientRef.child(FirebaseReference.CLIENT_REFERENCE).push().setValue(cl
ient);
}else{
Context context = getApplicationContext();
Toast.makeText(context, “No s’ha pogut registrat!”,
Toast.LENGTH_LONG).show();
}
}
});
}
Codi27: Mètode registrarClient.
Cal mencionar que s’utilitzarà la referència CLIENT_REFERENCE per dirigir el node a la carpeta
de clients.
La primera part del codi validarà com a usuari, mitjançant el correu i la contrasenya, a la base de
dades. Aquesta part de codi ve proporcionada per l’API d’autentificació de la Firebase la qual
30
podria anar acompanyada de diferents funcionalitats de la API d’autentificació, però això es
comentarà a l’apartat de línies futures.
La segona part del codi farà un get de tots els camps i el desarà a la base de dades, mitjançant
l’opció setVaule(). En aquesta part de codi cal remarcar dues coses: la primera és que al ser
nova informació, cal que els atributs estiguin acompanyats de la funció push() (aquesta funció
crearà nova informació a la base de dades). Si s’utilitzés, només es podria editar informació. La
segona part que cal remarcar, és que la nova informació estarà disposada a l’arrel de clients,
mitjançant la referència de clients. El formulari ve descrit per la figura 16.
Figura16: Estructura XML registreClient
31
IniciTaxista
Aquesta activity és pràcticament idèntica a la de IniciClient, però per l’altra banda de l’arbre.
Es té la mateixa configuració de login més registre, amb una temàtica diferent com es pot veure
a la figura 17.
Figura 17: Estructura XML iniciTaxista
Com s’ha vist, el botó iniciar farà les comprovacions pertinents per poder autentificar-se a la
base de dades i en cas positiu farà un intent a Taxista. Per altra banda si es prem el botó de
registrar, farà un intent cap a RegistreTaxista
32
RegistreTaxista
Registre taxista és molt similar a RegistreClient, tant l’estructura del codi Java com la del XML.
El disseny d’aquesta activity és el de la figura 18:
Figura18: Estructura formulari registreTaxista.
La principal diferència està en els camps a emplenar, ja que per fer la sol·licitud de taxista es
necessita un codi de taxi, que representa la llicència del taxista, i aquesta hauria de ser vàlida.
Com que el codi és pràcticament idèntic i l’únic que canvia és el tipus d’objecte, només es
comentarà la part dels atributs com es veu en el codi 28.
public class registreTaxista extends AppCompatActivity {
EditText mobil, codi, email, contrassenya, nom;
Button registrarTax;
FirebaseDatabase databaseTaxista = FirebaseDatabase.getInstance();
DatabaseReference taxRef = databaseTaxista.getReference();
Codi 28: Declaració registreTaxista.
Aquí es poden veure els diferents atributs que es tenen respecte als clients, i que la referència
utilitzada serà una referència cap a TAXISTA_REFERENCE. Per tant, l’objecte taxista es guardarà
dins la child de Taxistes
33
Client
En aquesta activity comencen les funcionalitats pròpies de l’aplicació. Aquí hi ha algunes de les
funcionalitats de ProvaObjectes, que pertoquen als clients.
La funcionalitat de geo-localització ens mostrarà l’usuari centrat en el mapa amb un punt blau
mitjançant el mètode del codi 29[8].
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
localitMeva = new LatLng(lati, lon);
if (lati == 0 && lon == 0) {
} else {
if (myMarker != null) {
myMarker.remove();
myMarker = mMap.addMarker(new
MarkerOptions().position(localitMeva).icon(BitmapDescriptorFactory.def
aultMarker(BitmapDescriptorFactory.HUE_BLUE)));
} else {
myMarker = mMap.addMarker(new
MarkerOptions().position(localitMeva).icon(BitmapDescriptorFactory.def
aultMarker(BitmapDescriptorFactory.HUE_BLUE)));
mMap.moveCamera(CameraUpdateFactory.newLatLng(localitMeva));
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(localitMeva, 10));
}
}
}
Codi 29: Mètode onMapReady
En aquest mètode es destaquen dos punts importants. Quan “refresquem” la posició d’un
marker, l’anterior es queda al mapa i, per tant, cada cop que es tingui un marker no null es
borrarà i se’n crearà un de nou. Per altra banda, quan es crea el marker, el mapa se centrarà en
localitMeva, que és la posició de l’usuari i se li assignarà un 10 al zoom, per fer-lo d’una mida
relativa a una ciutat. L’escala del zoom és inversa: 1 és el mapa mundi, 15 seria per veure una
zona petita d’una ciutat.
Hi ha dues formes d’executar el mètode, una introduint-lo al mètode onCreate i l’altra que sigui
una acció clickable. S’ha decidit executar-lo amb un botó per qüestions de comoditat com es veu
al codi 30.
iniciGps = (Button) findViewById(R.id.buttonIniciar);
iniciGps.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Context context = getApplicationContext();
// Acquire a reference to the system Location Manager
LocationManager locationManager = (LocationManager)
34
Client.this.getSystemService(Context.LOCATION_SERVICE);
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
// Called when a new location is found by the network
location provider.
gps = "" + location.getLatitude() + " " +
location.getLongitude();
lati = location.getLatitude();
lon = location.getLongitude();
onMapReady(mMap);
clientm.child(clientKey).child("pos").setValue(gps); //
Per escriure la pos
}
public void onStatusChanged(String provider, int status,
Bundle extras) {
}
public void onProviderEnabled(String provider) {
}
public void onProviderDisabled(String provider) {
}
};
Codi 30: Mètode onLocationChanged
Dins de l’onCreate tenim la funció principal que utilitza la base de dades realtime de Firebase.
Aquesta és la funcionalitat que ens pintarà els taxistes en el mapa tal i com es pot observar al
codi 31.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
// Obtain the SupportMapFragment and get notified when the map is
ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment)
getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
onChangedTaxistes();
mail = iniciClient.accesMail;
databaseClient = FirebaseDatabase.getInstance();
clientm = databaseClient.getReference("Clients");
clientm.orderByChild("email").equalTo(mail).addListenerForSingleValueE
vent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot childSnapshot :
dataSnapshot.getChildren()) {
clientKey = childSnapshot.getKey();
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
Codi 31: Estructura de lectura de la base de dades.
35
on onChangedTaxistes()és el codi 32.
public void onChangedTaxistes(){
taxistes = new ArrayList<>();
adapter = new Adapter(taxistes);
database = FirebaseDatabase.getInstance();
database.getReference("Taxistes").addValueEventListener(new
ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if( markerTaxistes != null){
mMap.clear();
myMarker = mMap.addMarker(new
MarkerOptions().position(localitMeva).icon(BitmapDescriptorFactory.def
aultMarker(BitmapDescriptorFactory.HUE_BLUE)));
}
taxistes.removeAll(taxistes);
for (DataSnapshot snapshot :
dataSnapshot.getChildren()) {
Taxistes taxista = snapshot.getValue(Taxistes.class);
taxistes.add(taxista);
lonTaxi = taxista.getLongitud();
latTaxi = taxista.getLatitud();
ocupat=taxista.isOcupat();
nom = taxista.getNom();
mobil = taxista.getMobil();
pintarTaxistes(mMap);
}
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Codi32: Mètode onChanedTaxistes
Aquest mètode crearà una llista de taxis. Per cada taxista creat cridarà a la funció
pintarTaxistes(mMap) i aquesta pintarà un marker al mapa de manera similar a com es
crea el marker particular. Cal mencionar que aquest mètode entra dins a la child de taxistes, i
per tant, al validar-se com a client, ha de tenir permisos a la bbdd per poder fer lectura de les
dades.
La funció pintarTaxistes(mMap) es troba en el codi 33.
public void pintarTaxistes(GoogleMap googleMap) {
mMap = googleMap;
LatLng localitzacioTaxi = new LatLng(latTaxi, lonTaxi);
if (latTaxi == 0 && lonTaxi == 0) {
} else {
if (ocupat!=false) {
markerTaxistes = mMap.addMarker(new
MarkerOptions().position(localitzacioTaxi).title("Nom :"+nom+" Mobil:
"+mobil).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFa
36
ctory.HUE_RED)));
} else {
markerTaxistes = mMap.addMarker(new
MarkerOptions().position(localitzacioTaxi).title("Nom :"+nom+" Mobil:
"+mobil).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFa
ctory.HUE_GREEN)));
}}}
Codi33: Mètode pintarTaxistes.
Aquí cal destacar que dins el marker se li pot donar informació, com ara el nom, el mòbil o
qualsevol dels atributs que disposem. S’ha fet un boolean per assignar de color vermell els taxis
ocupats i verd els lliures.
També cal mencionar que quan es fa el logout com a taxista, es posa la longitud i la latitud a 0;
per tant, si aquestes no tenen valor, no els dibuixarà el marker perquè si fos així, es tindrien
punters al punt 0,0 de la terra.
Per finalitzar aquesta classe, com que ara sí que s’està connectat, s’ha de fer un logout de la
base de dades i per tant s’assigna aquesta funcionalitat al botó de tornar enrere que tenen tots
els mòbils Android com es veu al codi 34 i tanca l’activity.
@Override
public void onBackPressed(){
FirebaseAuth.getInstance().signOut();
Context context = getApplicationContext();
Toast.makeText(context, "Desconectat", Toast.LENGTH_LONG).show();
clientm.child(clientKey).child("pos").setValue(null);
super.onBackPressed();
finish();}
Codi34: Mètode botó enderrera.
A l’apartat visual es volia representar una estètica simple, sense la necessitat d’utilitzar botons.
L’únic detall a mantenir seria el menú de dalt a la dreta així com els markers que representen
als taxistes; per tant, un mapa que ocupi tota la pantalla. Per facilitar la interacció amb el client
es van introduir botons com veiem a la figura 19.
37
Figura19: Estructura interfície Client
Taxista
L’apartat del taxista és el que dona informació, per tant, aquesta activity es basa en oferir una
posició al mapa i donar a conèixer la disponibilitat del taxi. Al ser una acitivty que només
proporciona informació, no té cap mètode a destacar; simplement canvia els booleans de certs
a falsos, o al inrevés, per seguir el seu codi de colors.
La posició la tractarem amb longitud i latitud per separat, perquè sigui més fàcil a l’hora de fer
els gets des de les altres fonts que necessiten aquesta informació.
El taxista tindrà un marker emergent segons el client que el sol·liciti; per a crear aquest marker
s’ha d’assignar dues variables noves al node del taxista de la Firebase, double latClient,
lonClient. Aquestes vindran donades mitjançant un push() des de l’activity del client.
38
2.5. Permisos Abans Android demanava tots els permisos a l’hora d’instal·lar l’aplicació. Això no tenia massa
sentit perquè hi ha permisos molt puntuals que l’aplicació fa servir en situacions molt concretes.
Per tant, per evitar haver de donar molts permisos a l’inici, van canviar el sistema per l’actual,
que es coneix com a sol·licitud durant l’execució, com es mostra en el codi 35. Aquest mètode
és molt més pràctic i segur per l’usuari, i la sol·licitud en el codi ha de ser just abans de requerir
el permís[9].
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_FINE_LOCATION);
if (permissionCheck == PackageManager.PERMISSION_DENIED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
android.Manifest.permission.ACCESS_FINE_LOCATION)) {
// Show an expanation to the user *asynchronously* -- don't
block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new
String[]{Manifest.permission.ACCESS_FINE_LOCATION},
1);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
Codi35: Permisos en execució.
En aquest codi també es poden afegir explicacions de perquè necessitem el permís. Per exemple,
és estrany que una aplicació et demani permís per poder llegir-te els missatges particulars. Però
tenint en compte que potser només és per validar-te automàticament el compte, tot pren més
sentit.
Aquest apartat va associat als permisos que declares en el manifest, però és l’usuari final qui
decideix si dona o no els permisos necessaris a l’aplicació.
39
2.6. Diagrames de relacions
Aquí es pot veure les diferents relacions entre les classes i les comunicacions que es fan amb els
diferents serveis. En la figura 20 es mostren les relacions entre classes.
Figura 20: Esquema de activitys
En figura 21 s’observa la comunicació que hi ha durant l’execució del programa.
Figura 21: Relació entre activitys i Firebase
40
Finalment es veu en la figura 23 la comunicació que es fa a l’hora de registrar un usuari.
Figura23: Comunicació amb els serveis d’autentificació.
41
2.7. Diagrama de seqüència En la figura 24 es pot observar pas per pas on anem després de cada activity.
Figura 24: Diagrama seqüencial.
42
3. BBDD
3.1. Organització
Com s’ha mencionat anteriorment, la base de dades escollida per aquest projecte és la que ens
ofereix Google, Firebase.
3.1.1. Pla Firebas
Aquesta base de dades està proporcionada en tres formats, segons el mètode de pagament:
gratuïta, una quantitat fixa o pagament per l’ús, tal i com se’ns mostra a la figura 25[10].
Figura25: Diferents modalitats de pagament de Firebase.
Com es pot veure a la imatge, s’ha seleccionat el pagament per ús. Això realment ho gestiona
Firebase. Inicialment es tenia l’opció gratuïta, tot i que Firebase va redistribuir el projecte a la
opció Blaze. Això no va ser problema perquè el pagament per ús inclou l’opció Spark i, si se
superés l’ús gratuït, es cobraria en funció del que s’excedís. S’ha de dir que és un punt negatiu
que no es pugui fer cent per cent responsable de l’opció que es tria, però com que l’opció Spark
dona uns mínims prou elevats per fer una aplicació de proves, es va ignorar el tema. Els
paràmetres més destacables segons les funcions escollides es mostren a la figura 26.
43
Figura26: Diferents característiques segons les modalitats de Firebase.
Com es pot apreciar, el pla Spark és ideal per fer projectes de desenvolupament, on totes les
comunicacions seran per fer proves. Si es llencés aquesta aplicació al mercat, caldria canviar el
model de la base de dades. En quant a volum de dades emmagatzemades, no s’utilitza gairebé
gens l’espai que ens ofereix, ja que es guarden uns quants Strings i doubles per cada usuari
registrat. Així que, en qualsevol dels casos, el factor limitant serien les connexions simultànies.
També cal mencionar que el pla Spark inclou el sistema d’autentificació que s’utilitzarà.
44
3.1.2. Utilitats que ens proporciona Firebase
Firebase és molt més que una base de dades, és una API que ens proporciona:
Authentication
Un sistema d’autentificació el qual s’utilitzarà en el sistema de logins. Aquest ens permet
seleccionar el mètode d’iniciar sessió. Com es veu en la figura 27 es pot accedir de diferents
maneres.
Figura27: Mètodes d’autentificació.
Per una qüestió de comoditat a l’hora de desenvolupar l’aplicació, es va decidir que es faria
mitjançant correu electrònic. Per habilitar o deshabilitar el mètode d’autentificació, cal fer-li
click.
45
També ens permet fer una llista blanca amb els dominis autoritzats tal i com es mostra a la figura
28:
Figura 28: Opcions d’admissió de l’aplicació
Per motius de seguretat i anti-spam es pot seleccionar, des del mateix menú, que només hi hagi
un compte per correu electrònic, i/o limitar els intents de creació de compte des d’una mateixa
IP, tal com es veu a la figura 29. Aquestes dues opcions estan deshabilitades perquè només
s’utilitzava una IP que creava nous comptes durant el període de desenvolupament i no té cap
sentit activar-les al temps que es crea l’aplicació. Però en un futur s’haurien d’incloure:
Figura 29: Sistema anti spam.
46
Es disposa d’un sistema de plantilles molt fàcil d’implementar on trobarem opcions com les de
la figura 30.
Figura 30: Sistema de plantilles de Firebase authenticator.
Aquestes plantilles permetran implementar sistemes com el canvi de contrasenya o email,
enviar un correu de verificació a l’hora de crear el compte, utilitzar el propi SMTP en comptes
de serveis de correu electrònic integrats, o fer la verificació de crear un compte mitjançant un
SMS.
Finalment trobem una pestanya d’ús on s’hi pot veure la gent autentificada en tot moment, en
una gràfica auto generada.
Database
Dins la part més important de la base de dades, hi tenim la Realtime Database on trobarem les
pestanyes d’opcions mostrades a la figura 31.
Figura 31: Opcions Database.
47
Datos
És on hi haurà l’arbre de les dades, el qual es pot expandir fins a qualsevol node.
Reglas
Són els permisos que tindran o no els usuaris, des del moment en què encara no estan registrats,
fins que estan autentificats.
Còpia de seguretat
Es crea una còpia de seguretat diària de les dades i les regles de la base de dades. Aquesta opció
es té desactivada perquè no hi ha cap informació de rellevància, però es pot activar de manera
gratuïta, mentre es tingui espai suficient al núvol.
Ús
Dibuixa els gràfics segons l’ús que està tenint a temps real, com en la figura 32.
Figura32: Mostra del flux de dades en temps real.
Storage
Serveix per emmagatzemar les dades i recuperar els arxius generats per l’usuari, com faríem
amb una copia de seguretat.
Hosting
Implementa llocs i aplicacions web mòbils en qüestió de segons, mitjançant una xarxa de
distribució de contingut segur i global.
ML Kit
Utilitza l’aprenentatge automàtic per solucionar problemes habituals en les aplicacions.
Crashlytics
És una solució potent i lleugera sobre informe d’errors.
Performance
Serveix per rebre informació útil sobre el rendiment de l’aplicació i sobre les latències que
experimenta l’usuari.
Test Lab
Prova l’aplicació en diferents dispositius.
48
Audiences
Ens fa comparatives de la base de dades amb altres èpoques de l’any o altres anys a la mateixa
època.
Latest Release
Gràfica d’usuaris que tenen l’última versió de l’aplicació.
Retention
Gràfic dels usuaris que obren l’aplicació cada dia.
StreamView
Pots veure la relació d’ús de l’aplicació en el mapa mundi.
DebugView
Informa d’errors que envien els usuaris.
Cloud Messaging
Administra les campanyes de notificacions i envia missatges per implicar als usuaris adequats,
en el moment més oportú.
AdMob
Mostra els anuncis de milions d’anunciants de Google i treu un rendiment econòmic de
l’aplicació.
Altres
Hi ha una gran llista d’utilitats com les anomenades anteriorment, però aquí només es
remarcaran les que tindrien un benefici directe i a curt termini en el cas que es llancés
l’aplicació al mercat.
Conclusió Firebase
Com es veu, disposem de molts serveis diferenciats en tres blocs:
Desenvolupament, qualitat, i analítics i de creixement.
D’aquests serveis s’utilitzaran principalment el d’Authentication i Database. Però és molt
interesant conèixer bé tots els altres, perquè són fàcilment configurables i aporten unes
possibilitats molt interesants, ja sigui el de AdMob, si es vol rendibilitzar una aplicació gratuïta;
el de DebugView, si es vol veure els diferents errors que hi ha en el codi, o el TestLab si es vol
assegurar que no hi ha incompatibilitats de l’aplicació a cap smarphone Android, per posar
alguns exemples. En línies futures seguirem parlant de totes aquestes possibilitats.
49
3.1.3. Sincronització firebase-aplicació
Com s’ha vist en l’apartat del gradle, la Firebase se sincronitza a l’aplicació mitjançant una
dependència en aquest, tal i com mostrem en el codi 36.
dependencies {
implementation 'com.android.support.constraint:constraint-
layout:1.1.2'
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-
core:2.2.2', {
exclude group: 'com.android.support', module: 'support-
annotations' })
compile 'com.android.support:appcompat-v7:25.3.1'
testCompile 'junit:junit:4.12'
compile 'com.google.firebase:firebase-core:9.0.0'
compile 'com.google.firebase:firebase-database:9.0.0'
compile 'com.google.firebase:firebase-auth:9.0.0'
implementation 'com.google.android.gms:play-services-maps:9.0.0'
compile 'com.google.android.gms:play-services-maps:9.0.0'
implementation 'com.android.support:recyclerview-v7:25.4.0'
}
Codi36: Versions de Firebase implementades.
S’ha utilitzat la versió 9.0.0 de Firebase, tant del core, com de la bbdd, com de l’authentificator,
perquè és la primera versió que s’ha trobat que no presentava cap error a l’hora de “construir”
el projecte. Es diu “construir”, o en anglès build, quan es compila el projecte i se sincronitzen
totes les seves dependències i les seves classes entre si.
Actualment les versions més noves dels complements de la Firebase van per la 16.0.1, però
també cal destacar que la documentació que hi ha per cada versió és diferent, i necessita una
versió consolidada i amb tota la informació disponible.
L’autentificació es realitza mitjançant l’email i la contrasenya.
Les referències de la base de dades estan en un arbre on hi ha dues branques: taxistes i clients,
i dins de cada branca hi ha directament els nodes amb els atributs de cada taxista o client, tal i
com es representa a la figura 33.
Figura33: Distribució dels nodes a la BBDD.
50
3.2. Comunicació
Per fer el sistema d’autentificació de Firebase hi ha dos sistemes: el sistema de tokens
personalitzats, i els sistema d’email i contrasenya.
S’ha utilitzat el sistema d’email i contrasenya per simplicitat, com es mostra al codi 37[11], ja
que hi ha els mètodes ja creats per fer el registre i el login. Per fer el registre es passa un email i
una contrasenya per paràmetre, i així es crea l’usuari a la Firebase. Aquesta funció es podria
completar fàcilment amb un email de validació com ja s’ha comprovat amb totes les
funcionalitats que ens dona Firebase, però, al ser un cas de desenvolupament, s’ometrà el pas
de validar tots els correus que es registren (ja que molts correus utilitzats són inventats). També
es veu que quan es completa la part del registre i es pot autentificar, salta al mètode
onComplete, que ens crearà un nou usuari amb els seus atributs associats al formulari i enviarà
un toast dient que s’ha completat el registre correctament. Si hi ha hagut qualsevol error,
tindrem un toast informant-nos que hem tingut un problema.
private void registrarClient(String emailr,String contra){
FirebaseAuth.getInstance().createUserWithEmailAndPassword(emailr,contr
a).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if(task.isSuccessful()){
Context context = getApplicationContext();
Toast.makeText(context, "S'ha registrat
correctament!", Toast.LENGTH_LONG).show();
Clients client = new
Clients(nom.getText().toString(),mobil.getText().toString(),
email.getText().toString(),null);
clientRef.child(FirebaseReference.CLIENT_REFERENCE).push().setValue(cl
ient);
}else{
Context context = getApplicationContext();
Toast.makeText(context, "No s'ha pogut registrat!",
Toast.LENGTH_LONG).show();
}
}
});
Codi37: Mètode registrarClient
Amb el login, la funció pre-definida és la del codi 38. En aquest cas, agafa l’email i la contrasenya
que hi ha a la pantalla i els contrasta amb els que té registrats a la base de dades. Un cop validat
fa un intent a l’activity següent.
51
private void iniClient(String mail, String pass){
FirebaseAuth.getInstance().signInWithEmailAndPassword(mail,pass).addOn
CompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if(task.isSuccessful()){
Intent intent = new Intent();
intent.setClass(getApplicationContext(),
Client.class);
startActivity(intent);
Context context = getApplicationContext();
Toast.makeText(context,
"Benvingut!"+email.getText().toString(), Toast.LENGTH_LONG).show();
accesMail = email.getText().toString();
}else{
Context context = getApplicationContext();
Toast.makeText(context, "Uauari o contrassenya no
vàlida!", Toast.LENGTH_LONG).show();
}
}
});
Codi38: Mètode de validació.
Es va descartar el sistema de tokens perquè en un principi semblava més complex a l’hora de
implementar-lo; de tota manera, es va trobar una funcionalitat que no ens permet el sistema
d’autentificació actual, així que en parlarem en l’apartat de línies futures.
A l’hora de desconnectar-se de la base de dades, hem de fer un logout, si no la comunicació
seguiria oberta en segon pla fins que es tanqués manualment l’aplicació; per tant es necessita
executar la comanda següent cada cop que sortim d’una activity en la qual hi estem autentificats
en una principal:
FirebaseAuth.getInstance().signOut();
52
4. Resultats
Aquí es tindrà una visita per totes les activitys resultants de l’aplicació un cop acabades totes les
funcionalitats i amb una interfície més enfocada a l’usuari final.
En la figura 34 es veu la pantalla d’inici.
Figura34: Activity Inici.
53
Si es prem Client, s’accedeix a la pantalla de login/registrar-se (figura 35) com a client.
Figura35: Activity IniciClient.
Si es prem Registrar, s’accedeix a la pantalla de registrar-se (figura 36).
Figura36: Activity RegistreClient.
54
Si es prem Iniciar amb les credencials correctes des de Inici client, s’inicia el programa com a
usuari (client) i s’anirà a una pantalla similar a la figura 37.
Figura37: Activity Client.
Si es prem Taxista des de Inici s’accedeix a la pantalla de login/registrar-se com a Taxista
(figura 38).
Figura38: Activity IniciTaxista.
55
Si es prem Registrar, s’accedeix a la pantalla de registrar-se com a taxista (Figura 39).
Figura39: Activity RegistreTaxista
Si es prem Iniciar amb les credencials correctes des de IniciTaxista, es comença l’activitat com a
usuari (taxista), i obtindrem una pantalla similar a la figura 40.
Figura40: Activity Taxista.
56
5. Conclusions i línies futures
5.1. Conclusions
5.1.1 Conclusió personal
En primer lloc m’agradaria dir que quan vaig plantejar aquesta aplicació sobre el paper, donava
per suposat tenir tots els coneixements necessaris per a dur a terme el projecte. Tenim unes
bones bases de Java, havíem cursat una assignatura d’introducció a l’Android, i havíem fet
diferents projectes en vàries assignatures amb l’ajuda del AppInventor. Agrupant aquests
coneixements, vaig suposar que seria una qüestió de temps escriure tot el codi necessari i que
trobaria prou documentació i aquesta no em costaria de trobar.
Un cop iniciat el projecte i feta la part més senzilla, que és la de navegar per dins l’aplicació, van
sorgir alguns problemes i em vaig adonar que fer una aplicació mòbil requereix d’una divisió de
tasques i d’un equip per dur-les a terme.
Per assolir els objectius vaig definir i desglossar el projecte en tasques. Dins aquests objectius
he intentat avançar tant com he pogut, malgrat que hi hagi coses que no he aconseguit acabar-
les del tot, com veurem en les línies futures.
La documentació no és tan fàcil de trobar com sembla, el volum d’aquesta és molt gran, però al
ser un tema que avança tan ràpid, només la documentació dels llocs oficials és fiable, ja que en
altres webs, probablement estigui desactualitzada. Això té un gran inconvenient, pots trobar-te
amb gent que ha tingut els mateixos problemes que tu però com que està en una altra versió de
Android o Firebase els seus coneixements i les seves solucions no et serveixin per res. I al mateix
temps, els llocs de documentació oficial manquen d’exemples per poder tenir les teves pròpies
referències.
Una de les coses que em va passar en relació al tema de com avança la tecnologia, és que vaig
començar el programa en Android 7.0 i a mig projecte es va actualitzar a 8.0. Òbviament tots els
avenços que guanyen els Androids en cada versió estan majoritàriament en facilitar les opcions
de desenvolupament. Però a vegades et toca canviar parts del teu codi per posar-los al dia,
perquè també es modifiquen parts del funcionament de les aplicacions perquè l’usuari tingui
una millor experiència.
Exposada la part de programació, és fàcil veure que per tenir una aplicació funcional i sòlida i
que aquesta estigui al dia, requereix d’ una bona divisió de tasques, un bon gràfic UML i un
equip de persones treballant en paral·lel en les diferents parts de l’aplicació.
57
5.1.2 Retrospectiva dels objectius plantejats i assolits.
Fent una repàs general de l’aplicació desenvolupada, es poden enumerar diferents objectius
bàsics per al funcionament d’aquesta, els quals s’han complert.
• S’ha dissenyat i creat l’estructura de classes de l’aplicació per un correcte ús tant en la
navegació com a l’hora d’utilitzar-la.
• S’ha dissenyat i creat l’estructura de la base de dades amb les seves respectives referències.
• S’ha completat amb èxit la integració de la base de dades al projecte.
• S’ha aconseguit una comunicació a temps real de l’aplicació al núvol on cada canvi que hi
hagi a la bbdd es notificarà a l’aplicació.
• S’ha implementat un sistema funcional de registres i logins on els usuaris podran crear el
seu perfil a la base de dades i posteriorment poder-se validar a l’aplicació.
• S’ha aconseguit una comunicació directa entre clients i taxistes.
5.2. Línies futures
M’agradaria canviar el sistema de login de “email més contrasenya” a “tokens personalitzats”
perquè és molt més fàcil referir-se a usuaris determinats amb els tokens, ja que amb el sistema
d’email has de buscar cada vegada la clau de l’usuari que està dins el node, fent comparacions
amb l’email. Amb el sistema de tokens, busques la ID de l’usuari directament.
En un inici, vaig plantejar d’una manera diferent la comunicació entre el client i el taxista de
forma que l’usuari, a l’hora de sol·licitar taxi, també li enviés la ruta d’inici a fi.
Complementàriament amb l’apartat anterior, també seria interessant, en una futura versió de
l’APP, integrar la funcionalitat de pagament mitjançant l’NFC del mòbil i completar aquesta
funcionalitat de forma que l’usuari pogués informar-se del preu segons el temps que retorna
l’API de Google maps i l’hora del dia, i així tingués un preu preestablert abans de sol·licitar la
ruta.
Per acabar m’agradaria acabar de polir les regles de la base de dades. Donar permisos a una
base de dades oberta als usuaris és complexa i s’ha de controlar qui pot accedir i/o modificar en
cada lloc; per tant, és un tema obert que s’hauria de regular com més acuradament millor.
58
6. Referències
[1] https://es.mytaxi.com/index.html
[2] https://firebase.google.com/?hl=es-419
[3] https://developer.android.com/studio/
[4] https://developers.google.com/places/web-service/get-api-key
[5] https://firebase.google.com/docs/database/android/start/?hl=es-419
[6] https://es.stackoverflow.com/
[7] https://firebase.google.com/docs/database/admin/retrieve-data?hl=es-419
[8]https://developers.google.com/android/reference/com/google/android/gms/maps/OnMap
ReadyCallback
[9] https://developer.android.com/training/permissions/requesting?hl=es-419
[10] https://firebase.google.com/pricing/?hl=es-419
[11] https://www.youtube.com/watch?v=C1GeJqu5Nz4