HW5: Trying out Firebase Firestore cloud storage
This homework is based on the project started in Lab 5 . You should complete the lab before proceeding. The goal is to get to know some basics of Firestore and switch out the Room local database (done in the lab) with a Firestore DB.
Setup
We will need to create a new Firebase project and use Firestore database for it. This requires a Google account.
- https://firebase.google.com/docs/firestore/quickstart is a good support material for the following steps and this homework in general!
Show steps for creating the Firebase project online
Go to https://console.firebase.google.com/ and create a new project
- You can turn off Analytics when asked
- When the project has been created, select the Android icon under "Get started by adding Firebase to your app ". At this stage you should also create a new Android Studio project or have the lab project handy.
- Provide the package name of the new App (cs.ut.ee.<<myAppName>> , for example). The one provided to Firebase MUST match the actual one used in the app!
- After proceeding, you will be presented a google-services.json file. This contains necessary data for your app to securely connect to Firebase.
- The registration wizard on the webpage provides guidelines how to add the .json to your Android Studio project. Follow the instructions.
- Follow the web instructions regarding SDK dependencies. Note that you have to modify 2 build.gradle files!
- For the app-level build.gradle dependencies{ .. },
implementation 'com.google.firebase:firebase-firestore-ktx:21.7.0'
is sufficient.
- For the app-level build.gradle dependencies{ .. },
Once you finish the above steps on firebase website, from the web console, add Firestore to your app (E.g. left-hand side menu Cloud Firestore -> Create Database )
- When asked about production or test mode, you can choose test mode, which gives read & write access to everybody who can reach your DB! Check here for more details on security in firestore.
- For region (firestore location), you can choose something in Europe.
Try to create a collection and add some documents from the web project manager.
Verifying it works (optional)
Optional part of the homework: Just for testing, let's try adding some data from our App's MainActivity (onCreate, for example).
- We will create a collection named "cities" and add the following Documents to it, using appropriate data types:
In MainActivity, try to access the database:
// Get instance of database val db = FirebaseFirestore.getInstance()
Then, add data of 3 documents to the "cities" collection with this code: (Show Code)
val cities = db.collection("cities") val dataTallinn = hashMapOf( "name" to "Tallinn", "population" to 434562, "area_km2" to 159 ) cities.document("TLL").set(dataTallinn) val dataTartu = hashMapOf( "name" to "Tartu", "population" to 93865, "area_km2" to 39 ) cities.document("TRT").set(dataTartu) val dataNarva = hashMapOf( "name" to "Narva", "population" to 55249, "area_km2" to 68 ) cities.document("NRV").set(dataNarva)
(You may comment out the code once it has been created on the first run, Firestore doesn't raise exceptions by default when inserting duplicate data).
After running your app with the above code, the data should become visible in the web dashboard of Firestore.
Task Requirements
Now, study the Firestore Getting Started Guide and other Docs and try to update the project so that the following functionality works and is using Firestore:
- Listing all recipes in the DB in MainActivity (2 pts)
- Should use a query that queries for all recipes (all Firestore documents in the recipes collection), but sorts them by preparation time!
- Creation of new Recipes ( 1pt)
- Insert 1 new document based on the user-entered values, should use a query similar to the 'cities' example above
- Display of Recipe Details view ( 2 pts)
- Should work based on a query that selects/filters by ID! (get a document by its ID)
- Note that Firestore document ID-s are Strings, not Ints
- Should work based on a query that selects/filters by ID! (get a document by its ID)
Tips
Some pointers
- How to query/read data and log it?
- How to sort results?
- What are documents and collections?
- Lecture 5 slides PDF has some basic examples of Firestore usage at the end.
Tip: Updating the UI/Activity/Adapter after getting Firebase query results
The Firebase library makes requests to the DB asynchronously and you get the results in callback methods (e.g. via .addOnSuccessListener { result -> //do something with the query result
} .
This means the UI should be notified that new data is available inside the callback, not right after the code block defining the request. In the lab, we set Room not make requests asynchronously, this means that code like this:
// MainActivity override fun onResume() { super.onResume() model.refresh() // uses DB to update model.recipeArray recipesAdapter.data = model.recipeArray recipesAdapter.notifyDataSetChanged()
}
will not work well, since the model.refresh() with firebase will do most of its work asynchronously, meaning the next lines of code which update the adapter, will be run before the DB query has finished.
Because we have not learnt how to manage background work properly yet, for this homework it is fine if you initiate the different Firestore queries from your Activities. You don't have to do it from the ViewModel, like we did in lab5 for Room.
Tip: Data Type returned by Firestore
Firestore returns documents as (Hash)Maps, so a straight-forward way is to manually construct RecipeEntities based on the various values (title, author, etc) contained in the HashMap.
Alternatively you could replace RecipeEntity entirely by HashMap. So, instead of having Array<RecipeEntity> in viewmodel, you might have Array<HashMap> instead.
Another option is to use tell Firebase which Custom data class (e.g. RecipeEntitiy) to translate the querie responses to, for more info see here: https://firebase.google.com/docs/firestore/query-data/get-data#custom_objects
- You should remove all previous Room-related classes and code (Dao, LocalRecipeDb). You can re-use the RecipeEntity data class, but remove the room @-annotations.
Other Troubleshooting
If you get an error about no of method references exceeding 65,536 methods after adding the Google services libraries, you probably have to enable multidex:
https://developer.android.com/studio/build/multidex#mdex-gradle