join firebase model and update live data with combined results - android

HelloBelow is my database structure
I want to first get results from post object and using "userid" from that I want to get results from Users Object.
I want to update ViewModel with the above results that contain fields that are in both the objects
How to achieve this ?
I have written code to get the result from post object but how to again make call to get user object and update viewmodel and livedata object
private static final DatabaseReference POST_REF =
FirebaseDatabase.getInstance().getReference("/Post");
private final FirebaseQueryLiveData liveData = new
FirebaseQueryLiveData(POST_REF);
#NonNull
public LiveData<DataSnapshot> getDataSnapshotLiveData() {
return liveData;
}

There are different ways to model you database to archive this, It is always good to follow best practices see: https://firebase.google.com/docs/database/android/structure-data#best_practices_for_data_structure
In this case, since you only want the Profile Pic and name I would save it directly into the object:
{
"Posts":{
"post1":
{
"likes":23,
"userId":"id",
"user":
{
"imageUrl":"url",
"name":"Name"
}
}
}
}
Of course, the tradeoff is that the image URL won’t be updated if the user node gets updated (unless you code something to update it in a Cloud Function for example)
On the other hand, you could also perform those two calls to the Firebase Realtime Database (one to get the post, and the other to get the user data):
ValueEventListener postListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Post object and use the values to update the UI
Post post = dataSnapshot.getValue(Post.class);
ValueEventListener userListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot userDataSnapshot) {
post.setUser(userDataSnapshot.getValue(User.class);)
}
};
mUserReference.addListenerForSingleValueEvent(userListener);//only fetch data once
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
};
mPostReference.addValueEventListener(postListener);
EDIT:
Having the user object inside the post would work using the architecture components (Just be sure to have the proper class to deserialize), on the other hand, since you are using the FirebaseQueryLiveData https://firebase.googleblog.com/2017/12/using-android-architecture-components.html and may want to avoid writing the user on that post node, I think you can have both ViewModels with different Database references, and once the data is fetched you could just update the post object e.g. post.setUser(user) with the user obtained from the other ViewModel Observer and then update the UI. You could also have a HashMap to keep track of what post needs what user, although this answer looks like a way to go: https://stackoverflow.com/a/46483213/1537389. Hope that helps

Related

How to set activity layout based on user type / role?

I am working on an Android app with 2 types of users (doctors and patients), and I want each type to have their own UI. For eg, doctors must see ' Add days off' and patients must see ' Book appointment' . Somehow I don't get anywhere with whatever I try.
I also use Firebase Auth and Realtime Database which makes the user type retrieval kinda tricky. So far I've tried a lot of Async classes, methods, variables, shared preferences, retrieving data while on launcher splash screen.
The best I got is getting the user to login, it shows the good layout, then I start the app again and it shows the wrong layout. Somehow I noticed it just works on the second run, but not always so the behaviour is unpredictable to me at least. But at least the user type from the database is retrieved.
I have a class that extends Application, which checks if there's an user authenticated and then redirects the user to either LoginActivity, or MainMenuActivity.
I have created a method that retrieves the Firebase Auth user data from Realtime Database, looping through both Doctors and Patients 'children' until it finds the current user email and gets its type. Since Realtime Database is asynchronous, the methos gets an interface as an argument, and after the loop, the I call the interface's method, which sets a static boolean variable (isUserDoctor).
Before setting the content view (with 2 possible layouts), I call the function described before and it works the way I first mentioned, which is not good.
The method that retrives data
public void getUserType(final DataStatus dataStatus) {
currentUser = FirebaseAuth.getInstance().getCurrentUser();
currentUserEmail = currentUser.getEmail();
databaseReference = FirebaseDatabase.getInstance().getReference("Users");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
currentUserType.clear();
FirebaseManager.isUserDoctor = false;
DataSnapshot allDoctors = dataSnapshot.child("Doctors");
DataSnapshot allPatients = dataSnapshot.child("Patients");
for (DataSnapshot ds : allDoctors.getChildren()) {
if (currentUserEmail.equals(Utils.decodeUserEmail(ds.getKey()))) {
currentUserType.add(ds.child("userType").getValue().toString());
} else {
for (DataSnapshot dsPacient : allPatients.getChildren()) {
if (currentUserEmail.equals(Utils.decodeUserEmail(dsPacient.getKey()))) {
currentUserType.add(dsPacient.child("userType").getValue().toString());
}
}
}
}
dataStatus.DataIsLoaded(currentUserType.get(0).toString());
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
The interface
public interface DataStatus {
void DataIsLoaded(String userType);
}
The method's call in Main Menu
FirebaseManager.getInstance().getUserType(new DataStatus() {
#Override
public void DataIsLoaded(String userType) {
if ("doctor".equals(userType))
FirebaseManager.isUserDoctor = true;
else
FirebaseManager.isUserDoctor = false;
}
});
if (FirebaseManager.isUserDoctor)
setContentView(R.layout.activity_main_menu_doctor);
else
setContentView(R.layout.activity_main_menu);
So if anyone has any ideas about how to show the proper layout and allow functions based on user role/type please share. What I basically need is to retrieve the userType from the current email just in time to set a variable needed throughout the whole app in order to hide/show certain views.

Retrieve String out of addValueEventListener Firebase

I want to receive a string from addValueEventListener() method I use to resell the data from the database Firebase. The data arrive correctly.
But when certain to get the string out of that method to use it in another, it returns nothing.
You have tips?
I already tried putExtras and also create a method on purpose but it did not work.
final DatabaseReference mPostReference = FirebaseDatabase.getInstance().getReference().child("user-daily").child(getUid()).child("2017-Year");
mPostReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final ArrayList<String> labels = new ArrayList<String>();
for (DataSnapshot data : dataSnapshot.getChildren()){
final DailyItem dailyItem = data.getValue(DailyItem.class);
labels.add(dailyItem.mese);
}
title.setText(labels.get(position));
a = title.getText().toString();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(view.getContext(),"database error",
Toast.LENGTH_SHORT).show();
}
});
//this return null... why?
String title = a;
The data is loaded from Firebase asynchronously. By the time you run title = a, the onDataChange method hasn't been called yet. Set some breakpoints in a debugger to verify this, it's key to understanding how asynchronous loading works.
The solution is to reframe your problem from "first get the object, then do blabla with the title" to "start getting the object; once the object is available, do blabla with the title".
In code this translates to:
final DatabaseReference mPostReference = FirebaseDatabase.getInstance().getReference().child("user-daily").child(getUid()).child("2017-Year");
mPostReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final ArrayList<String> labels = new ArrayList<String>();
for (DataSnapshot data : dataSnapshot.getChildren()){
final DailyItem dailyItem = data.getValue(DailyItem.class);
labels.add(dailyItem.mese);
}
title.setText(labels.get(position));
// Do blabla with the title
String title = title.getText().toString();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(view.getContext(),"database error",
Toast.LENGTH_SHORT).show();
}
});
Many developers new to Firebase (and other modern web APIs, as they all work this way) struggle with this problem. So I recommend you also check out their questions and answers:
Cannot access firebaseObjectObservable outside of set
Android Firebase get value of child without DataChange
Value of a global variable is reset after it is initialised in ValueEventListener
can't get values out of ondatachange method
ArrayList not updating inside onChildAdded function
Setting Singleton property value in Firebase Listener
and most others in this list of search results
In order to retrieve the string from method addValueEventListener in viewmodel or any other network call, it is recommended to use the either MutableLiveData<T> or LiveData<T> and observe the same in your activity. Observer will observe the changes, and as soon as string got filled up, the observer method will automatically give you string which you are looking.
You need to create reference variable for the LiveData<T> reference_variable wherever your addValueEventLister is located and set its value in your addValueEventListener.
And then in your viewmodel create the returning value function like below...
Observe this function in your activity and you will have your string.
public MutableLiveData<TotalRunsWicketsAndData> getDisplayableDetails() {
return observableLiveData;
}
I am using MutableLiveData here.
This is a trick which does it. It would be easy to do so if you have less data to retrieve from ValueEventListener.
Inside the onDataChange(), use a setText to set the required value in it. Keep the visibility of this text view as "Gone". Then retrieve using getText outside the ValueEventListener.
You can retrieve the whole list by using GenericTypeIndicator. Follow the official guide on here

How to retrieve data from firebase database without DataSnapshot? #AskFirebase

This my database in firebase
I want to get the value of page which is equal to 200.
When I tried like that:
DatabaseReference my= userid_database.child("Books").child("Book 1").child("Page");
System.out.println(my);
Output:
https://my-proje-book-timer.firebaseio.com/qpPLvgpM0TMF6vz4Kqw8QaYroG83/Books/Book%201/Page
When I tried like that:
DatabaseReference my= userid_database.child("Books").child("Book 1").child("Page").getKey();
System.out.println(my);
Output:
Page
I don't want to use this:
my.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String text = dataSnapshot.getValue(String.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Because I want to get all values for each Book pages and make a calculation with these values. In this way, I couldn't get the values outside of the method.
If you don't want to use a listener, you can use the REST API to easily make an HTTP query to find the data you want, assuming that it's readable without authentication. If you need authentication, it gets more difficult.
But you're really better off just learning now to make the listeners work for you, even if you're not accustomed to that kind of processing.

FireBase Android Need 1 value saved under a single user

I have the following data structure on firebase for the user MF0qeRA4p7djfjgXxqwFOck3m6p02. I want to get the value of item3 to populate a single field into the User interface on an Android App. I have been looking through samples on Stackoverflow, but all I have found are outdated and do not work with the current version of firebase. I'm new to firebase completely and this is my first app on android. I've got the oncreate user method to populate the users email address and add the 4 item fields, but retrieving the data I'm completely lost and I am not sure where to even begin.
-Users
---MF0qeRA4p7djfjgXxqwFOck3m6p02
------item1:"1"
------item2:"2"
------item3:"3"
------item4:"4"
According to what I can identify is, you are facing problem retrieving data from this reference. Here is the code:
final DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference("Users");
databaseReference.child("MF0qeRA4p7djfjgXxqwFOck3m6p02").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Map<String, Object> map=(Map<String, Object>)dataSnapshot.getValue();
String item3=(String)map.get("item3");
display(item3);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Hope this helps.
You can create a custom model and inside you can insert elements. Something like this:
public class Item {
private List<Object> ojects;
}
There you can save instance of Item on database. In this case you have more controll. Other case is to use push() method, that will generate a new encoded key, something like this:
mDatabase.child("items").push().put(new Object());

Saving data from Firebase into a list

I'm currently developing an Android app that deals with storing book translations. I'm using Firebase to hold all the data online, and I have no trouble pulling data from it and storing them as Book objects. I have a master list of books in the main class that stores all the books from the Firebase database, however the list doesn't seem to update properly.
public static List<Book> books = new ArrayList<Book>();
protected void onCreate(Bundle savedInstanceState) {
myFirebaseRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot bookSnapshhot : snapshot.getChildren()) {
Book book = bookSnapshhot.getValue(Book.class);
books.add(book);
System.out.println(book.getAuthor() + " - " + book.getTitle());
for (Chapter chapter : book.getChapters()) {
System.out.println(chapter.getTitle());
}
for (Genre genre : book.getGenres()) {
System.out.println(genre.getGenre());
}
System.out.println(books.size());
}
}
#Override public void onCancelled(FirebaseError error) { }
});
System.out.println(books.size());
}
In that body, the size of books is correct (right now it is only 1). However if I call the same print statement outside of that code body (the last line), it gives me 0. I don't know why, because it successfully adds it inside the onDataChange() method but it seems to not apply the changes on the outside.
I think that your problem is about the asyncronously of the ValueEventListener.
The onDataChange is called asynconously.
You have to use the books after the onDataChange is called.
The addValueEventListener() will fetch the data from Firebase asynchronously.
What your code depicts is that you are adding ValueEventListener and then printing System.out.println(books.size());. This means your logs gets displayed after you add ValueEventListener(which doesn't means your listener gets invoked). At this point your listener is not invoked yet and so isn't onDataChange(). Thus you won't ever get updated count outside the listener scope.

Categories

Resources