On my Android app I'm reading data from the Firebase Realtime Database.
Edit: I changed this using callbacks, because that works asynchronously.
I'm trying to use a ViewModel class to store the data from Firebase Database, and to update my Google Maps UI with new markers.
The problem is that I need to subscribe my LiveData object in onCreate(), but I do need to update the map's UI with markers in onMapReady().
Do I need to use callbacks here or maybe the ViewModel is redundant here? Any suggestions could be helpful. I'm trying to work it out for few days with not be able success, in addition I was looking for a lot of questions and answers online and nothing seems to be helpful.
readData function from DatabaseManager:
fun readFromDatabase(type: String, callBack: OwnerListCallBack) {
when (type) {
OWNERS -> {
ownerRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
// This method is called once with the initial value and again
// whenever data at this location is updated.
for (snapshot: DataSnapshot in dataSnapshot.children) {
val owner = snapshot.getValue(Owner::class.java)
Log.d(TAG, "readFromDatabase: onDataChange: owner's name is ${owner?.name}")
if (owner != null)
owners.add(owner)
Log.d(TAG, "readFromDatabase: onDataChange: owner's name is ${owners[0].name}")
}
}
override fun onCancelled(error: DatabaseError) {
// Failed to read value
Log.w(TAG, "Failed to read value.", error.toException())
}
})
}
}
setRestaurantsOnMap function which called in onMapReady:
private fun setRestaurantsOnMap() {
Log.d(TAG, "setRestaurantsOnMap: called")
// Set markers on the map. The owner's restaurants.
database.readFromDatabase(OWNERS)
// Log.d(TAG, "setRestaurantsOnMap: ${owners[0]}")
if (database.owners.isNotEmpty()) {
for (owner in database.owners) {
try {
val ownerAddress = owner.getRestaurant().getAddress()
Log.d(TAG, "setRestaurantsOnMap: owner's address is: ${ownerAddress.getAddress()}")
Toast.makeText(this, "setRestaurantsOnMap: owner's address is: ${ownerAddress.getAddress()}", Toast.LENGTH_LONG).show()
val latlng = getLocationFromAddress(this, ownerAddress.getAddress())
Log.d(TAG, "setRestaurantsOnMap: latitude is - ${latlng?.latitude}, longitude is - ${latlng?.longitude} ")
Toast.makeText(this, "setRestaurantsOnMap: latitude is - ${latlng?.latitude}, longitude is - ${latlng?.longitude} ", Toast.LENGTH_LONG).show()
if (latlng != null) {
mMap.addMarker(MarkerOptions()
.position(latlng)
.title(owner.getRestaurant().getName())
.snippet(owner.getRestaurant().getAddress().getAddress())
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_CYAN))
)
mMap.moveCamera(CameraUpdateFactory.newLatLng(latlng))
} else {
Toast.makeText(this, "setRestaurantsOnMap: latlng is null", Toast.LENGTH_SHORT).show()
}
} catch (e: NullPointerException) {
e.printStackTrace()
} catch (e: IndexOutOfBoundsException) {
e.printStackTrace()
}
}
}
}
My ViewModel class:
class MapsViewModel : ViewModel() {
private val db = DatabaseManager()
private val owners = MutableLiveData<List<Owner>>()
val ownerList: LiveData<List<Owner>>
get() = owners
init {
owners.postValue(loadOwners())
Log.d(TAG, "init: Owners are ${owners.value}")
}
override fun onCleared() {
Log.d(TAG, "onCleared: canceling pending downloads")
}
private fun loadOwners(): List<Owner> {
var ownerList = ArrayList<Owner>()
Log.d(TAG, "loadAddress: called")
// Set markers on the map. The owner's restaurants.
db.readFromDatabase(OWNERS, object : OwnerListCallBack {
@SuppressLint("RestrictedApi")
override fun onCallBack(owners: ArrayList<Owner>) {
if (owners.isNotEmpty()) {
ownerList = owners
Log.d(TAG, "Owners are ${ownerList[0]}, ${ownerList[1]}, ${ownerList[2]}")
} else {
throw DatabaseException("Loaded was not success. Number of owners is ${owners.count()}")
}
}
})
return ownerList
}