AbhinayMe
AbhinayMe

Reputation: 2547

How to show data "user_devices" device_name in RecyclerView based on "devices" child from Firebase Realtime Database

Here is my sample Firebase Realtime DB

{
  "devices": {
    "123456": {
      "device_mac_id": "123456",
      "device_name": "BOX 12",
      "id": "2"
    },
    "222222": {
      "device_mac_id": "222222",
      "device_name": "BOX 22",
      "id": "3"
    },
    "888888": {
      "device_mac_id": "888888",
      "device_name": "BOX 88",
      "id": "6"
    }
  },
  "users": {
    "UcI1REoXAoSC7mg4u7RGbw8gR2g2": {
      "email_id": "[email protected]",
      "full_name": "Ab Cd",
      "id": "UcI1REoXAoSC7mg4u7RGbw8gR2g2",
      "user_devices": {
        "-MIbRGtT25ki36foJ6b5": "888888",
        "-MIc-ORsWRqO45OJrBXg": "123456"
      }
    }
  }
}

DashboardFragment.java

{
    showProgressBar();

    deviceModelArrayList.clear();
    devicesAdapter.notifyDataSetChanged();

    mDatabaseRef
            .child("users")
            .child(FirebaseAuth.getInstance().getUid())
            .child("user_devices")
            .orderByKey()
            .addChildEventListener(
                    new ChildEventListener() {
                        @Override
                        public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
                            Log.e(TAG,
                                    "onChildAdded:"
                                            + " snapshot:" + snapshot.getValue()
                                            + " previousChildName:" + previousChildName);
                            getDeviceData(snapshot.getValue(String.class));
                        }

                        @Override
                        public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
                            Log.e(TAG,
                                    "onChildChanged:"
                                            + " snapshot:" + snapshot.getValue()
                                            + " previousChildName:" + previousChildName);
                        }

                        @Override
                        public void onChildRemoved(@NonNull DataSnapshot snapshot) {
                            Log.e(TAG,
                                    "onChildRemoved:"
                                            + " snapshot:" + snapshot.getValue());
                        }

                        @Override
                        public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
                            Log.e(TAG,
                                    "onChildMoved:"
                                            + " snapshot:" + snapshot.getValue()
                                            + " previousChildName:" + previousChildName);
                        }

                        @Override
                        public void onCancelled(@NonNull DatabaseError error) {
                            Log.e(TAG,
                                    "onCancelled:"
                                            + " error:" + error.toString());
                        }
                    });
}


private void getDeviceData(String deviceID) {
    if (deviceID != null) {
        mDatabaseRef
                .child("devices")
                .child(deviceID)
                .addListenerForSingleValueEvent(
                        new ValueEventListener() {
                            @Override
                            public void onDataChange(DataSnapshot dataSnapshot) {
                                hideProgressBar();

                                // This method is called once with the initial value and again
                                // whenever data at this location is updated.
                                UserModel.DeviceModel deviceModel = dataSnapshot.getValue(UserModel.DeviceModel.class);
                                Log.e(TAG, "onDataChange: devices: " + deviceModel.getDevice_name());

                                deviceModelArrayList.add(deviceModel);
                                devicesAdapter.notifyDataSetChanged();
                            }

                            @Override
                            public void onCancelled(DatabaseError error) {
                                hideProgressBar();
                                // Failed to read value
                                Log.w(TAG, "Failed to read value.", error.toException());
                            }
                        }
                );
    }
}

I want to show in RecyclerView but data keep on multiplying as onDataChanged, I'm adding object to ArrayList. Please suggest me if I'm wrong or best practices.

Upvotes: 0

Views: 58

Answers (2)

AbhinayMe
AbhinayMe

Reputation: 2547

Thank you @Alex for your timely answer.

Meanwhile waiting for answers, I did some research on FirebaseUI for Realtime Database

dependencies {
// FirebaseUI for Firebase Realtime Database
implementation 'com.firebaseui:firebase-ui-database:x.y.z'
}

I felt more comfortable with FirebaseRecyclerAdapter as my challenge was dealing with handling Lists.

Here is my code snippets that worked for me.

firebaseRecyclerOptions =
            new FirebaseRecyclerOptions
                    .Builder<String>()
                    .setQuery(mDatabaseRef.child("users").child(mFirebaseAuth.getUid()).child("user_devices"), String.class)
                    .build();

    firebaseRecyclerAdapter =
            new FirebaseRecyclerAdapter<String, DevicesViewHolder>(firebaseRecyclerOptions) {

                @Override
                protected void onBindViewHolder(@NonNull DevicesViewHolder holder, int position, @NonNull String deviceID) {
                    if (deviceID != null) {
                        mDatabaseRef
                                .child("devices")
                                .child(deviceID)
                                .addListenerForSingleValueEvent(
                                        new ValueEventListener() {
                                            @Override
                                            public void onDataChange(DataSnapshot dataSnapshot) {
                                                hideProgressBar();

                                                // This method is called once with the initial value and again
                                                UserModel.DeviceModel deviceModel = dataSnapshot.getValue(UserModel.DeviceModel.class);
                                                Log.e(TAG, "onDataChange: devices: " + deviceModel.getDevice_name());

                                                holder.b.btnPowerBox.setText(deviceModel.getDevice_name());
                                            }

                                            @Override
                                            public void onCancelled(DatabaseError error) {
                                                hideProgressBar();
                                                // Failed to read value
                                                Log.w(TAG, "Failed to read value.", error.toException());
                                            }
                                        }
                                );
                    }

                    holder.b.btnPowerBox.setOnClickListener(view -> {
                        Log.d(TAG, "onBindViewHolder: Button clicked at position " + position);
                    });
                }

                @NonNull
                @Override
                public DevicesViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                    ItemDevicesBinding bItem = ItemDevicesBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
                    return new DevicesViewHolder(bItem);
                }

                @Override
                public void onDataChanged() {
                    // If there are no chat messages, show a view that invites the user to add an item to list.
                    b.tvEmptyListMessage.setVisibility(getItemCount() == 0 ? View.VISIBLE : View.GONE);
                }
            };
    firebaseRecyclerAdapter.startListening();
    b.rvDevices.setAdapter(firebaseRecyclerAdapter);

Upvotes: 0

Alex Mamo
Alex Mamo

Reputation: 139039

According to your last comment:

Yes, there are 3 devices but I have assigned only 2 to the user.

To get the DeviceModel objects that correspond only to a specific user, you need to loop through the DataSnapshot object using the getChildren() method twice, as shown in the following lines of code:

String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userDevicesRef = rootRef.child("users").child(uid).child("user_devices");
ValueEventListener valueEventListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for(DataSnapshot userDeviceSnapshot : dataSnapshot.getChildren()) {
            String deviceId = userDeviceSnapshot.getValue(String.class);
            DatabaseReference userDevicesRef = rootRef.child("devices").child(deviceId);
            ValueEventListener eventListener = new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot deviceSnapshot) {
                    UserModel.DeviceModel deviceModel = dataSnapshot.getValue(UserModel.DeviceModel.class);
                    Log.d("TAG", deviceModel.getDevice_name());
                }

                @Override
                public void onCancelled(@NonNull DatabaseError databaseError) {
                    Log.d("TAG", databaseError.getMessage()); //Don't ignore potential errors!
                }
            };
            userDevicesRef.addListenerForSingleValueEvent(eventListener);
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {
        Log.d("TAG", databaseError.getMessage()); //Don't ignore potential errors!
    }
};
userDevicesRef.addListenerForSingleValueEvent(valueEventListener);

The result in the logcat will be:

BOX 88
BOX 12

But this is only to see that is working. In your case, you should add those objects to a the list and notify the adapter about changes.

Upvotes: 1

Related Questions