Hi all I can't think of a better example to illustrate my point so do let me know If my example has some errors. But hopefully this example will get my point through.
class A {
String CATEGORY = "A";
public String getCATEGORY() {
return CATEGORY;
}
}
class B extends A {
String CATEGORY = "B";
#Override
public String getCATEGORY() {
return CATEGORY;
}
}
class C extends A {
String CATEGORY = "C";
#Override
public String getCATEGORY() {
return CATEGORY;
}
}
public class MyClass {
private List<A> array = Arrays.asList(new A(), new B(), new C());
public MyClass() {}
}
Now if I upload MyClass onto firebase using setValue for example, firebase will show me the properties of class A, B and C. However, when I read the data from firebase and call sth like getValue(MyClass.class) the List it returns me are all of type A and the subclasses are not preserved. Is there a workaround to allow firebase to preserve the class types uploaded?
If you use Firebase's default serializer, it simply writes all public properties and fields to the database. Say that you store a single instance of each class, it'd be:
-L1234567890: {
cATEGORY: "A"
},
-L1234567891: {
cATEGORY: "B"
},
-L1234567892: {
cATEGORY: "C"
},
There won't be enough knowledge in the database for the SDK to reinflate the correct sub-class. While you and I can see that the cATEGORY value matches the class name, the Firebase SDK has no such knowledge.
It won't be too hard to write your own custom deserializer for this data though, taking a DataSnapshot with the values above and reinflating the correct class and values.
You could also do a hybrid: detect the class type directly, and then tell Firebase what class to read:
String cat = snapshot.child("cATEGORY").getValue(String.class)
Class clazz = "C".equals(cat) ? C.class : "B".equals(cat) ? B.class : A.clas;
A object = snapshot.getValue(clazz);
Hi I am developing a chat application using Firebase database.
below is the structure of my database for user on Firebase
The structure of my model classes for User currently is
public class User {
Credential credentials;
List<Conversation> conversationList;
}
public class Credential implements Parcelable {
private String name="";
private String email="";
private String profilePicLink="";
}
I am not getting what should be the structure of conversations list as per the structure on firebase db .
TIA.
Update the rules for your database to allow reads and writes from all users,
{
"rules": {
".read": "true",
".write": "true"
}
}
Reference:
https://firebase.google.com/docs/database/android/read-and-write
https://firebase.google.com/docs/database/rest/retrieve-data
Firebase stores its data as key-value pair rather than a list. As per your snapshot, a user has two main Hashmaps i.e. conversations and credentials. Therefore, you can create your models as:
public class User {
Hashmap<String, String> credentials;
Hashmap<String, Hashmap<String, String>> conversationList;
}
public class Credential implements Parcelable {
private String name="";
private String email="";
private String profilePicLink="";
public void setModel(Hashmap<String, String> data) {
//assign values to your model here
}
}
I push data to Firebase using Order object, the question is I want the first letter of every child name capital. I defined the property like "Complain" but in Firebase it still shows as "complain", I dont know how to make it.
The current structure of the Firebase:
The structure I want:
I defined the property like this:
#Data
public class Order implements Serializable {
#SerializedName("Complain")
private String Complain;
public Order() {
Complain = "";
}
public String getComplain() {
return Complain;
}
public void setComplain(String complain) {
Complain = complain;
}
}
I push data to Firebase like this:
Map<String, Object> map = new HashMap<>();
map.put(orderSavePath, order);
reference.updateChildren(map).addOnCompleteListener(listener);
The Firebase JSON serialization name is controlled by the annotation PropertyName.
public class Order implements Serializable {
private String Complain;
public Order() {
Complain = "";
}
#PropertyName("Complain")
public String getComplain() {
return Complain;
}
#PropertyName("Complain")
public void setComplain(String complain) {
Complain = complain;
}
}
The annotation needs to be on both the getter and the setter. Alternatively you can just use public fields and reduce the class to:
public class Order {
#PropertyName("Complain")
public String Complain;
}
I am trying to pull a list of friend IDs from my firebase database into my app, and then look up any additional data associated with the player (such as username, online status etc..) by looking up their entry under "users" using their unique ID
My schema looks as follows:
{friends
{
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"-KR0aTQGT6pfRfB5qIUz" : {
"friend_id" : "6vFVAAQfwiYERl03C3lzxdPjnEp2"
},
"-KR0aaMAOS3FWOAosBmo" : {
"friend_id" : "kxrQFVjGv0XUHyV5N764Nq50Q3J3"
}
}
}
}
The first unique ID is the ID of the player, which enables querying their friends list. The child objects of that represent the ID of the relationship, and the friend_id under that shows the other players (the friends) ID
The user schema looks as follows:
{
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : {
"emailAddress" : "b#b.com",
"level" : 1,
"userName" : "steve"
},
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"emailAddress" : "a#a.com",
"level" : 1,
"userName" : "bob"
},
"kxrQFVjGv0XUHyV5N764Nq50Q3J3" : {
"emailAddress" : "bg#b.com",
"level" : 1,
"userName" : "tim"
},
"rNtYvwF8LBhTRM1Wk8ybBJyrFIg2" : {
"emailAddress" : "c#c.com",
"level" : 1,
"userName" : "test account"
}
}
Now, in my app, I can successfully pull all of the friend_id entries, but am not sure how to then turn around and pull additional information on the friend by using this ID. Ideally I would be able to query each friend one by one by their unique player ID, and populate the friends list fragment I have using a firebaseListAdapter.
This is how I am pulling the friend IDs and populating the list object.
ValueEventListener friendListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
friendIdList.clear();
for (DataSnapshot messageSnapshot: dataSnapshot.getChildren()) {
String friend_id = (String) messageSnapshot.child("friend_id").getValue();
friendIdList.add(friend_id);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
mDatabase.addValueEventListener(friendListener);
Could anybody help me figure out the best way to pull all of this additional information on the friends once the IDs are received from the initial query?
You can use ChildEventListener to get all friends then on your populateview or onBindViewHolder use ValueEventListener to get all data of a friend. also try to use FirebaseRecyclerView instead of FirebaseListView
Yes agreed even I have same confusion.
What are we struggling is with Contains[*, *, *] query.
Even I have a similar problem
/Users{
"uuid1":{
data1:"value1"
data2:"value2"
}
"uuid2":{
data1:"value1"
data2:"value2"
}
}
/Friends:{
uuid1:{
"uuid2":true
....
}
uuid2:{
"uuid1":true
...
}
}
My Query is how to Query list of only my friends and present their data("/User") with FirebaseRecyclerViewAdapter.
I'd suggest a different data structure. Here it is based on your data:
{
friends : {
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : true,
"kxrQFVjGv0XUHyV5N764Nq50Q3J3" : true
}
}
}
Then loop over each one of them and pull up their individual profiles.
So I'm just going to connect the answers given by Mathew Berg and pastillas. Based on your question and comments I think combined they provide the solution you are looking for.
Data Structure
The structure you are using for your users location looks good to me so I'd say you can leave that as is:
{
users: {
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : {
"emailAddress" : "b#b.com",
"level" : 1,
"userName" : "steve"
},
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"emailAddress" : "a#a.com",
"level" : 1,
"userName" : "bob"
}
}
}
For you friends location I agree with the structure given by Mathew Berg:
{
friends : {
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : true,
"kxrQFVjGv0XUHyV5N764Nq50Q3J3" : true
}
}
}
Just a quick FYI, you don't need to use a Boolean value with this structure. You can use any allowed data type as the value for each key. In this instance the key is what's important, the value is just a place holder because Firebase doesn't allow keys with null values. That said if you find a reason to use a value that is more useful you could do that. In my opinion using a Boolean value makes the structure a little more readable by team members or someone who may follow behind you.
Retrieving Your Data
There are multiple ways you can query a friends list and get the data for each user. Since you referred to using a FirebaseListAdapter in your question I'll go with that. The same pattern can be used with a FirebaseRecyclerAdapter if you want to use choose to use a RecyclerView with FirebaseUI instead. The basic steps are:
Create a DatabaseReference that points to the location of the user
who's friends list you want.
Create a DatabaseReference that points to your users location
Create your FirebaseListAdapter.
When you override populateView get the key for each item
Use a addListenersingleValueEvent() to retrieve each friends user information and populate your listview row items with the data from
this query.
Within onDataChange of value event listener use the info for each user to populate views for each list item.
DatabaseReference mFriendsRef = FirebaseDatabase.getInstance().getReference().child("friends").child(someUserID;
DatabaseReference mUsersRef = FirebaseDatabase.getInstance().getReference().child("users");
...
mAdapter = new FirebaseListAdapter<YourModel>(this, YourModel.class, android.R.your_layout, mFriendsRef) {
#Override
protected void populateView(final View view, YourModel model, int position) {
String key = getRef(position).getKey();
mUsersRef.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//get data from dataSnapshot and bind to views for each list item
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
messagesView.setAdapter(mAdapter);
Check out the comment made on this question for concerns about performance when using this approach: Android Firebase - Denormalized Queries using FirebaseUI
Sorry sir, I dont know if it will work on FirebaseUI but here is my code that I think has the same problem and also same solution, it will display the list of a user followers then if a follower clicked it will open and pass the id of item that is clicked to other activity. I did not use FirebaseUI.
public class FollowersAdapter extends RecyclerView.Adapter<FollowersAdapter.FollowersHolder>{
public ArrayList<Follower> followers;
public void addFollower(Follower follower) {
followers.add(follower);
notifyItemInserted(getItemCount());
}
public FollowersAdapter(Context context) {
this.followers = new ArrayList<>();
mDatabase = FirebaseDatabase.getInstance().getReference();
this.fUser = FirebaseAuth.getInstance().getCurrentUser();
mDatabase.child("follower").child(fUser.getUid()).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Follower follower = dataSnapshot.getValue(Follower.class);
addFollower(follower);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Follower follower = dataSnapshot.getValue(Follower.class);
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public FollowersHolder onCreateViewHolder(ViewGroup parent, int position) {
View view = inflater.inflate(R.layout.followers_model, parent, false);
FollowersHolder holder = new FollowersHolder(view);
return holder;
}
#Override
public void onBindViewHolder(final FollowersHolder holder, final int position) {
Query userQuery = mDatabase.child("users").child(followers.get(position).getId());
userQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
user = dataSnapshot.getValue(User.class);
holder.name.setText(user.getName());
final Bundle extras = new Bundle();
extras.putString(EXTRAS_POSTER_ID, user.getId());
holder.cont.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!fUser.getUid().equals(user.getId())) {
Intent intent = new Intent(context, ViewUserProfile.class);
intent.putExtra(EXTRAS_BUNDLE, extras);
context.startActivity(intent);
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public int getItemCount() {
return followers.size();
}
class FollowersHolder extends RecyclerView.ViewHolder {
View cont;
TextView name;
public FollowersHolder(View itemView) {
super(itemView);
cont = itemView.findViewById(R.id.cont_followers);
name = (TextView) itemView.findViewById(R.id.tv_followers_name);
}
}
}
you get the data using childEventListener then on the onBindViewHolder or populateView add onClickListener and pass the id when it clicked to other activity. sorry sir im a beginner lol :)
I have recently been working with Android development. I have been developing a social networking app. For the app, I decided to create a separate helper class for all database methods. In my database, all users have a user id and their information is stored under this id. I have a (non-static) method in this class that would get certain User information when given a DatabaseReference to the user's information location. The method would simply take the reference, add a listener for single value event (addListenerForSingleValueEvent(ValueEventListener)). I was encountering problems with this so I tried putting a Log statement in the onDataChange() method of the ValueEventListener. Oddly enough, this Log method was never reached. Even more strange is the fact that, if I copy and paste the code from this method into one of the locations where I need it, the Log statement is reached. Does anyone have any idea as to why this happens? This is a method that I am using in multiple activities and copying and pasting the code everywhere would make the code very sloppy. Any help would be much appreciated. Thank you!
Update: It turns out the code works if placed in the Database class, but the Log statement will only run after the method is over. Below is the an outline of the class I am using to observe this.
Fragment Class
public class FragmentClass extends Fragment {
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDatabase = DatabaseManager.getInstance();
String userId = "userId";
mDatabase.getUserFromUserId(userId);
}
}
Database Class
public class DatabaseManager {
private static FirebaseDatabaseManager mInstance;
private static FirebaseDatabase mDatabase;
public static FirebaseDatabaseManager getInstance() {
if(mInstance == null) {
mInstance = new FirebaseDatabaseManager();
}
return mInstance;
}
private FirebaseDatabaseManager() {
mDatabase = FirebaseDatabase.getInstance();
}
public void getUserFromUserId(final String userId) {
DatabaseReference userReference = mDatabase.getReference(userId);
userReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.i("databaseTag", "reached");
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.i("databaseTag", "reached");
}
});
while(true) { // if this part is commented out, the log statement will be executed; otherwise, it won't
}
}
}