Crea un'app per il Weight Tracker con Cloud Firestore

La memorizzazione dei dati della tua app nel cloud è molto importante in questi giorni perché gli utenti tendono a possedere più dispositivi e desiderano sincronizzare le loro app su tutti. Con Cloud Firestore, un database NoSQL in tempo reale disponibile sulla piattaforma Firebase, farlo è più facile e sicuro che mai.

In un precedente tutorial, ti ho presentato tutte le potenti funzionalità che Cloud Firestore ha da offrire. Oggi ti mostrerò come utilizzarlo insieme ad altri prodotti Firebase, come FirebaseUI Auth e Firebase Analytics, per creare un'app per body tracker semplice, ma altamente scalabile.

Prerequisiti

Per seguire questo tutorial passo passo, avrai bisogno di:

  • l'ultima versione di Android Studio
  • un account Firebase
  • e un dispositivo o emulatore con Android 5.0 o versioni successive

1. Impostazione del progetto

Per poter utilizzare i prodotti Firebase nel tuo progetto Android Studio, avrai bisogno del plug-in Gradle di Google Services, un file di configurazione di Firebase e alcuni implementazione dipendenze. Con l'Assistente Firebase, puoi ottenerli tutti molto facilmente.

Apri l'assistente andando a Strumenti> Firebase. Quindi, selezionare il analitica opzione e fare clic su Registra un evento di Analytics collegamento.

Ora puoi premere il tasto Connetti a Firebase pulsante per connettere il tuo progetto Android Studio a un nuovo progetto Firebase.

Tuttavia, per aggiungere effettivamente il plugin e il implementazione dipendenze, dovrai anche premere il tasto Aggiungi Analytics alla tua app pulsante.

L'app Weight Tracker che stiamo creando oggi avrà solo due funzioni: memorizzare i pesi e visualizzarli come una lista ordinata in ordine cronologico inverso. Naturalmente, utilizzeremo Firestore per conservare i pesi. Per visualizzarli come elenco, tuttavia, utilizzeremo i componenti relativi a Firestore disponibili nella libreria FirebaseUI. Pertanto, aggiungere quanto segue implementazione dipendenza dal App modulo di build.gradle file:

implementazione 'com.firebaseui: firebase-ui-firestore: 3.2.2'

Gli utenti devono essere in grado di visualizzare solo i propri pesi, non i pesi di tutti coloro che utilizzano l'app. Pertanto, la nostra app deve avere la capacità di identificare in modo univoco i propri utenti. FirebaseUI Auth offre questa capacità, quindi aggiungi la dipendenza seguente:

implementazione 'com.firebaseui: firebase-ui-auth: 3.2.2'

Avremo anche bisogno di alcuni widget di Material Design per dare alla nostra app un aspetto gradevole. Quindi assicurati di aggiungere la libreria del supporto di progettazione e la libreria dei dialoghi del materiale come dipendenze.

implementazione 'com.android.support:design:26.1.0' implementazione 'com.afollestad.material-dialogs: core: 0.9.6.0'

Infine, premi il Sincronizza ora pulsante per aggiornare il progetto.

2. Configurazione dell'autenticazione Firebase

Firebase Authentication supporta vari provider di identità. Tuttavia, tutti sono disabilitati per impostazione predefinita. Per abilitarne uno o più, devi visitare la console di Firebase.

Nella console, seleziona il progetto Firebase che hai creato nel passaggio precedente, vai al suo Autenticazione sezione e premere il tasto Imposta il metodo di accesso pulsante.

Per consentire agli utenti di accedere alla nostra app utilizzando un account Google, abilitare Google come fornitore, dare un nome significativo al pubblico al progetto e premere il tasto Salvare pulsante.

Google è il provider di identità più semplice che puoi utilizzare. Non ha bisogno di configurazione e il tuo progetto Android Studio non avrà bisogno di ulteriori dipendenze per questo.

3. Configurare Cloud Firestore

Devi abilitare Firestore nella console Firebase prima di iniziare a usarlo. Per farlo, vai al Banca dati sezione e premere il Iniziare pulsante presente nel Cloud Firestore Beta carta.

Verrà ora richiesto di selezionare una modalità di sicurezza per il database. Assicurati di scegliere il Inizia in modalità bloccata opzione e premere il tasto Abilitare pulsante.

Nella modalità bloccata, per impostazione predefinita, nessuno sarà in grado di accedere o modificare i contenuti del database. Pertanto, ora è necessario creare una regola di sicurezza che consenta agli utenti di leggere e scrivere solo quei documenti che appartengono a loro. Inizia aprendo il Regole linguetta.

Prima di creare una regola di sicurezza per il nostro database, dobbiamo finalizzare il modo in cui archiviamo i dati al suo interno. Quindi diciamo che avremo una collezione di primo livello chiamata utenti contenenti documenti che rappresentano i nostri utenti. I documenti possono avere ID univoci identici agli ID che il servizio di autenticazione Firebase genera per gli utenti.

Poiché gli utenti aggiungeranno diverse voci di peso ai loro documenti, l'utilizzo di una sub-raccolta per archiviare tali voci è l'ideale. Chiamiamo la sotto-raccolta pesi.

Sulla base dello schema sopra, ora possiamo creare una regola per il percorso utenti / user_id / pesi / peso. La regola sarà che un utente è autorizzato a leggere e scrivere sul percorso solo se il ID utente la variabile è uguale all'ID di autenticazione Firebase dell'utente.

Di conseguenza, aggiorna il contenuto dell'editor delle regole.

service cloud.firestore match / databases / database / documents match / users / user_id / weights / weight consenti lettura, scrittura: if user_id == request.auth.uid; 

Infine, premi il Pubblicare pulsante per attivare la regola.

4. Autenticazione degli utenti

La nostra app deve essere utilizzabile solo se l'utente ha effettuato l'accesso utilizzando un account Google. Pertanto, non appena viene aperto, deve verificare se l'utente ha un ID di autenticazione Firebase valido. Se l'utente ha l'ID, dovrebbe andare avanti e rendere l'interfaccia utente. In caso contrario, dovrebbe visualizzare una schermata di accesso.

Per verificare se l'utente ha un ID, possiamo semplicemente controllare che il utente corrente proprietà del FirebaseAuth la classe non è nulla. Se è nullo, possiamo creare un intento di accesso chiamando il createSignInIntentBuilder () metodo del AuthUI classe.

Il codice seguente mostra come farlo per Google come provider di identità:

if (FirebaseAuth.getInstance (). currentUser == null) // Accedi startActivityForResult (AuthUI.getInstance (). createSignInIntentBuilder () .setAvailableProviders (arrayListOf (AuthUI.IdpConfig.GoogleBuilder (). build ())). build ( ), 1) else // Già registrato in showUI ()

Si noti che stiamo chiamando un metodo chiamato ShowUI () se un ID valido è già presente. Questo metodo non esiste ancora, quindi crealo e lascia il suo corpo vuoto per ora.

divertimento privato showUI () // To do

Per cogliere il risultato dell'intento di accesso, dobbiamo ignorare il onActivityResult () metodo dell'attività. All'interno del metodo, se il valore del resultCode argomento è RESULT_OK e il utente corrente la proprietà non è più nulla, significa che l'utente è riuscito ad accedere correttamente. In questo caso, dobbiamo chiamare di nuovo il ShowUI () metodo per rendere l'interfaccia utente.

Se l'utente non riesce ad accedere, possiamo visualizzare un brindisi e chiudere l'app chiamando il finire() metodo.

Di conseguenza, aggiungi il seguente codice all'attività:

override fun onActivityResult (requestCode: Int, resultCode: Int, data: Intent?) super.onActivityResult (requestCode, resultCode, data) if (requestCode == 1) if (resultCode == Activity.RESULT_OK && FirebaseAuth.getInstance () .currentUser! = null) // Firma con successo in showUI () else // Accedi a Toast.makeText non riuscito (questo, "Devi accedere per continuare", Toast.LENGTH_LONG) .show () finish () 

A questo punto, se esegui l'app per la prima volta, dovresti riuscire a visualizzare una schermata di accesso simile a questa:

Nelle esecuzioni successive, grazie a Google Smart Lock, che è abilitato per impostazione predefinita, l'accesso verrà eseguito automaticamente.

5. Definizione dei layout

La nostra app richiede due layout: uno per l'attività principale e uno per le voci di peso che verranno visualizzati come elementi dell'elenco cronologico inverso.

Il layout dell'attività principale deve avere a RecyclerView widget, che fungerà da elenco e a FloatingActionButton widget, che l'utente può premere per creare una nuova voce di peso. Dopo averli posizionati entrambi all'interno di a RelativeLayout widget, il file XML di layout della tua attività dovrebbe assomigliare a questo:

     

Abbiamo associato un gestore eventi su clic denominato addWeight () con il FloatingActionButton widget di. Il gestore non esiste ancora, quindi crea uno stub per questo all'interno dell'attività.

divertimento addWeight (v: View) // To do

Per mantenere il layout della voce di peso semplice, ne avremo solo due TextView i widget al suo interno: uno per visualizzare il peso e l'altro per visualizzare l'ora in cui è stata creata la voce. Usare un LinearLayout widget come contenitore per loro sarà sufficiente.

Di conseguenza, creare un nuovo file XML di layout denominato weight_entry.xml e aggiungere il seguente codice ad esso:

    

6. Creazione di un modello

Nel passaggio precedente, hai visto che ogni voce di peso ha un peso e tempo associati ad essa. Per far sapere a Firestore di questo, dobbiamo creare un modello per l'immissione di peso.

I modelli di Firestore sono in genere semplici classi di dati con le variabili membro richieste.

classe di dati WeightEntry (var weight: Double = 0.0, var timestamp: Long = 0)

Ora è anche un buon momento per creare un titolare della vista per ogni voce di peso. Il titolare della vista, come avrai intuito, sarà utilizzato da RecyclerView widget per rendere gli elementi della lista. Quindi crea una nuova classe chiamata WeightEntryVH, che estende il RecyclerView.ViewHolder classe e creare variabili membro per entrambi TextView widgets. Non dimenticare di inizializzarli usando il findViewById () metodo. Il codice seguente mostra come fare in modo conciso:

class WeightEntryVH (itemView: View?): RecyclerView.ViewHolder (itemView) var weightView: TextView? = itemView? .FindViewById (R.id.weight_view) var timeView: TextView? = itemView? .findViewById (R.id.time_view)

7. Creazione di documenti utente unici

Quando un utente tenta di creare una voce di peso per la prima volta, la nostra app deve creare un documento separato per l'utente all'interno di utenti collezione su Firestore. Come abbiamo deciso in precedenza, l'ID del documento non sarà altro che l'ID di autenticazione Firebase dell'utente, che può essere ottenuto utilizzando il uid proprietà del utente corrente oggetto.

Per ottenere un riferimento al utenti collezione, dobbiamo usare il collezione() metodo del FirebaseFirestore classe. Possiamo quindi chiamare il suo documento() metodo e passare il uid come argomento per creare il documento dell'utente.

Avremo bisogno di accedere ai documenti specifici dell'utente sia durante la lettura e la creazione delle voci di peso. Per evitare di codificare la logica precedente due volte, ti suggerisco di creare un metodo separato per questo.

private fun getUserDocument (): DocumentReference val db = FirebaseFirestore.getInstance () val users = db.collection ("users") val uid = FirebaseAuth.getInstance (). currentUser !!. uid return users.document (uid)

Si noti che il documento verrà creato solo una volta per utente. In altre parole, più chiamate al metodo sopra restituiranno sempre lo stesso documento, a condizione che l'utente utilizzi lo stesso account Google.

8. Aggiunta di voci di peso

Quando gli utenti premono il pulsante di azione mobile della nostra app, devono essere in grado di creare nuove voci di peso. Per consentire loro di digitare i loro pesi, creiamo ora una finestra di dialogo contenente un Modifica il testo widget di. Con la libreria Material Dialog, farlo è estremamente intuitivo.

Dentro il addWeight () metodo, che funge da gestore eventi on-click del pulsante, crea a MaterialDialog.Builder istanza e chiamatela titolo() e soddisfare() metodi per dare al tuo dialogo un titolo e un messaggio significativo. Allo stesso modo, chiama il InputType () metodo e passare TYPE_CLASS_NUMBER come argomento per assicurarsi che l'utente possa digitare solo numeri nella finestra di dialogo.

Quindi, chiama il ingresso() metodo per specificare un suggerimento e associare un gestore di eventi alla finestra di dialogo. Il gestore riceverà il peso che l'utente ha digitato come argomento.

Infine, assicurati di chiamare il mostrare() metodo per visualizzare la finestra di dialogo.

MaterialDialog.Builder (this) .title ("Aggiungi peso") .content ("Qual è il tuo peso oggi?") .InputType (InputType.TYPE_CLASS_NUMBER o InputType.TYPE_NUMBER_FLAG_DECIMAL) .input ("weight in pounds", "", false, _, peso -> // da fare) .show ()

All'interno del gestore di eventi, ora dobbiamo aggiungere il codice per creare e compilare effettivamente un nuovo documento con voce di peso. Perché il documento deve appartenere al pesi raccolta del documento univoco dell'utente, per poter accedere alla raccolta, è necessario chiamare il collezione() metodo del documento restituito da getUserDocument () metodo.

Una volta che hai la collezione, puoi chiamarla Inserisci() metodo e passare una nuova istanza di WeightEntry class ad esso per memorizzare la voce.

getUserDocument () .collection ("weights") .add (WeightEntry (weight.toString (). toDouble (), Date (). time))

Nel codice sopra, puoi vedere che stiamo usando il tempo proprietà del Data classe per associare un timestamp alla voce.

Se esegui l'app ora, dovresti essere in grado di aggiungere nuove voci di peso a Firestore. Non li vedrai ancora nell'app, ma saranno visibili nella console di Firebase.

9. Visualizzazione delle voci di peso

Ora è il momento di popolare il RecyclerView widget del nostro layout. Quindi inizia creando un riferimento usando il findViewById () metodo e assegnando una nuova istanza di LinearLayoutManager classe ad esso. Questo deve essere fatto all'interno del ShowUI () metodo che abbiamo creato in precedenza.

val weightsView = findViewById(R.id.weights) weightsView.layoutManager = LinearLayoutManager (this)

Il RecyclerView il widget deve visualizzare tutti i documenti presenti all'interno di pesi raccolta del documento dell'utente. Inoltre, gli ultimi documenti dovrebbero apparire per primi. Per soddisfare questi requisiti, dobbiamo ora creare una query chiamando il collezione() e ordinato da() metodi.

Per motivi di efficienza, è possibile limitare il numero di valori restituiti dalla query chiamando il limite() metodo.

Il codice seguente crea una query che restituisce le ultime 90 voci di peso create dall'utente:

val query = getUserDocument (). collection ("weights") .orderBy ("timestamp", Query.Direction.DESCENDING) .limit (90)

Usando la query, ora dobbiamo creare un FirestoreRecyclerOptions oggetto, che useremo in seguito per configurare l'adattatore del nostro RecyclerView widget di. Quando passi il domanda istanza al setQuery () metodo del suo costruttore, assicurati di specificare che i risultati restituiti sono sotto forma di WeightEntry oggetti. Il seguente codice mostra come farlo:

val options = FirestoreRecyclerOptions.Builder() .setQuery (query, WeightEntry :: class.java) .setLifecycleOwner (this) .build ()

Potresti aver notato che stiamo rendendo la nostra attuale attività il proprietario del ciclo di vita del FirestoreRecyclerOptions oggetto. Ciò è importante perché vogliamo che il nostro adattatore risponda in modo appropriato agli eventi del ciclo di vita comuni, come l'apertura o la chiusura dell'utente.

A questo punto possiamo creare un FirestoreRecyclerAdapter oggetto, che utilizza il FirestoreRecyclerOptions oggetto per configurarsi. Perché il FirestoreRecyclerAdapter la classe è astratta, Android Studio dovrebbe sovrascrivere automaticamente i suoi metodi per generare codice simile a questo:

val adapter = object: FirestoreRecyclerAdapter(opzioni) override fun onBindViewHolder (titolare: WeightEntryVH, posizione: Int, modello: WeightEntry) // To do override fun onCreateViewHolder (parent: ViewGroup ?, viewType: Int): WeightEntryVH // To do

Come puoi vedere, il FirestoreRecyclerAdapter la classe è molto simile al RecyclerView.Adapter classe. Di fatto, ne deriva. Ciò significa che puoi usarlo nello stesso modo in cui utilizzeresti il RecyclerView.Adapter classe.

Dentro il onCreateViewHolder () metodo, tutto ciò che devi fare è gonfiare il weight_entry.xml file di layout e ritorno a WeightEntryVH visualizzare l'oggetto titolare in base ad esso.

val layout = layoutInflater.inflate (R.layout.weight_entry, null) return WeightEntryVH (layout)

E dentro il onBindViewHolder () metodo, è necessario utilizzare il modello argomento per aggiornare i contenuti del TextView widget che sono presenti all'interno del titolare della vista.

Durante l'aggiornamento del weightView il widget è semplice, aggiornando il TimeView il widget è leggermente complicato perché non vogliamo mostrare direttamente il timestamp, che è in millisecondi, all'utente.

Il modo più semplice per convertire il timestamp in una data e un'ora leggibili è usare il FormatDateTime () metodo del DateUtils classe. Oltre al timestamp, il metodo può accettare diversi flag diversi, che verrà utilizzato per formattare la data e l'ora. Sei libero di usare le bandiere che corrispondono alle tue preferenze.

// Mostra weight holder.weightView? .Text = "$ model.weight lb" // Mostra data e ora val formattedDate = DateUtils.formatDateTime (applicationContext, model.timestamp, DateUtils.FORMAT_SHOW_DATE o DateUtils.FORMAT_SHOW_TIME o DateUtils.FORMAT_SHOW_YEAR ) holder.timeView? .text = "On $ formattedDate"

Infine, non dimenticare di indicare il RecyclerView widget per l'adattatore appena creato.

weightsView.adapter = adattatore

L'app è pronta. Ora dovresti essere in grado di aggiungere nuove voci e vederle comparire nella lista quasi immediatamente. Se esegui l'app su un altro dispositivo con lo stesso account Google, vedrai apparire anche le stesse voci di peso.

Conclusione

In questo tutorial hai visto quanto sia facile e veloce creare un'app completamente funzionale per l'analisi del peso per Android utilizzando Cloud Firestore come database. Sentiti libero di aggiungere più funzionalità! Ti suggerisco anche di provare a pubblicarlo su Google Play. Con il piano Firebase Spark, che offre attualmente 1 GB di spazio di archiviazione dati gratuito, non avrai problemi a servire almeno qualche migliaio di utenti.

E mentre sei qui, dai uno sguardo ad alcuni dei nostri altri post sullo sviluppo di app per Android!