Cant Add value Into Array In OnSuccess() - java

I'm really new at android development, and trying to make app with firebase. I made signup with profile photo and pushed photo name into Database. And file to FireStore. At the bottom Code mImageUrls.add(uri.toString()); line doesn't work. But it makes toast in OnSuccess(), just I can't add data into array. I initialized Array in OnCreateView(final ArrayList<String> mImageUrls = new ArrayList<>();) like other arrays too. I need your help.
lv = (ListView) rootview.findViewById(R.id.lv_main);
final ArrayAdapter adapter = new MyAdapter(getActivity(), name_list,date,imgs);
myref = FirebaseDatabase.getInstance().getReference().child("MusiciansNonSensitive");
final ArrayList<String> mImageUrls = new ArrayList<>();
final StorageReference storageReference = FirebaseStorage.getInstance().getReference();
myref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot postSnapshot: snapshot.getChildren()) {
String name_value = postSnapshot.child("name").getValue().toString();
String province_value = postSnapshot.child("province").getValue().toString();
final String url_path = postSnapshot.child("photourl").getValue().toString();
StorageReference photo_url = storageReference.child("uploads/"+url_path);
photo_url.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
mImageUrls.add(uri.toString()); // I tried to add Log.d and it adds value every for loop, but after loop it being empty array again.
}
});
//mImageUrls.add("A");
name_list.add(name_value);
date.add(province_value);
}
Log.d("LOGGOGOGOOG",mImageUrls.toString()); // Here being empty
//Toast.makeText(getContext(),list.toString(),Toast.LENGTH_LONG).show();
lv.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});

addOnSuccessListener is asynchronous and returns immediately. The callback is invoked some time later, even after the loop completes and your call to setAdapter. You're going to have to rewrite the code to only set the adapter after all the URLs have been fetched asynchronously. You can wait for a bunch of tasks to complete by using Tasks.whenAll() to get a new Task that will complete after the list of tasks you provide are fully complete.

Related

Duplicate child when i call push() in Firebase Realtime Database

I am trying to retrieve data from Firebase Realtime Database and add this data to a listview. When I call push() firebase generates two children (one inside the other) with the same unique key. This is the structure of my database:
database
That is how I save the data:
RunningSession runningSession = new RunningSession(date, activityTime, pace, timeElapsed,
finalDistance, image, tipe);
DatabaseReference reference = databaseReference.child("users").child(userUid).child("activities").push();
Map<String, Object> map = new HashMap<>();
map.put(reference.getKey(),runningSession);
reference.updateChildren(map);
This is how i retrieve the data (results in a null pointer exception):
DatabaseReference reference = databaseReference.child("users").child(userId).child("activities");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
list.clear();
for (DataSnapshot snpshot : snapshot.getChildren()) {
RunningSession run = snpshot.getValue(RunningSession.class);
list.add(run);
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
ListViewAdapter adapter = new ListViewAdapter(this, list);
ListView activityItems = (ListView) findViewById(R.id.activityList);
activityItems.setAdapter(adapter);
You are getting duplicate push IDs because you are adding them twice to your reference. If you only need one, then simply use the following lines of code:
RunningSession runningSession = new RunningSession(date, activityTime, pace, timeElapsed, finalDistance, image, tipe);
DatabaseReference reference = databaseReference.child("users").child(userUid).child("activities");
String pushedId = reference.push().getKey();
reference.child(pushedId).setValue(runningSession);
The code for reading that may remain unchanged.

(Android App) Is there a way to retrieve data from a Firebase database while inside the SnapshotParser part of the FirebaseRecyclerOptions query?

So, as the title says, I am looking to retrieve data from a Firebase database before constructing an object in FirebaseRecyclerOptions to be used in a FirebaseRecyclerAdapter. Basically, what I am trying to do is make a friends list in an app I'm working on. This what the database looks like:
Friends:
uid1:
id: friendID
uid2:
id: friendID
Users:
uid1:
name: name
status: status
image: profileImageUrl
uid2:
name: name
status: status
image: profileImageUrl
I've got code that currently looks like this:
FirebaseRecyclerOptions<Users> options = new FirebaseRecyclerOptions.Builder<Users>().setQuery(usersDatabase, new SnapshotParser<Users>() {
#NonNull
#Override
public Users parseSnapshot(#NonNull DataSnapshot snapshot) {
System.out.println(snapshot);
rootRef.child("Users").child(snapshot.getValue().toString()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
System.out.println(dataSnapshot);
name = dataSnapshot.child("name").getValue().toString();
status = dataSnapshot.child("status").getValue().toString();
image = dataSnapshot.child("image").getValue().toString();
return;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
System.out.println(snapshot);
return new Users(name, image, status);
}
}).build();
The problem is that the ValueEventListener I add does not trigger until after the new Users instance is returned. Should I be adding the ValueEventListener to the same DatabaseReference (userDatabase) as the FirebaseRecyclerOptions query?
What you're trying to do isn't really possible with FirebaseUI. The snapshot parser needs to return a User object immediately, or synchronously. You can't perform an asynchronous database query (which does not complete immediately, or even guaranteed to complete at all) in order to provide that value.
If you need to perform multiple queries in order to populate your views, you won't be able to use FirebaseUI effectively. You should probably consider doing all your lookups ahead of time, or write a special adapter that allows view contents to be populated asynchronously as the results become available. This will end up being a lot of code to do correctly.
It perhaps seems a little redundant to be answering my own question, but this is mostly for anyone else that has trouble with this. Following #Doug Stevenson's suggestion, I started trying to make my own custom recycler adapters and options class. However, I realized that the queries for the options could be modified. So basically, the solution is this:
Query query = database.collection("Users");
#Override
protected void onStart() {
super.onStart();
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DocumentReference ref = FirebaseFirestore.getInstance().collection("Users").document(uid);
ref.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()){
DocumentSnapshot document = task.getResult();
if (document.exists()) {
friends = (ArrayList<String>) document.get("friends");
if (friends.size() > 0) {
FirestoreRecyclerOptions<Users> options = new FirestoreRecyclerOptions.Builder<Users>().setQuery(query.whereIn("user_id", friends), Users.class).build();
FirestoreRecyclerAdapter<Users, UsersViewHolder> adapter = new FirestoreRecyclerAdapter<Users, UsersViewHolder>(options) {
#Override
public UsersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_user_layout, parent, false);
return new UsersViewHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull UsersViewHolder usersViewHolder, int i, #NonNull Users users) {
if (users != null) {
usersViewHolder.setName(users.name);
usersViewHolder.setStatus(users.status);
usersViewHolder.setImg(users.image);
final String userID = getSnapshots().getSnapshot(i).getId();
usersViewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent profilePage = new Intent(FriendsList.this, ProfileActivity.class);
profilePage.putExtra("userID", userID);
startActivity(profilePage);
}
});
}
}
};
usersListPage.setAdapter(adapter);
adapter.startListening();
}
}
}
}
});
}
In the setQuery method, rather than using the collection reference as the query, I created a query object from it, and then modified the query query.whereIn(), which allows you to check to see if the field of a document contains the given object or one of the objects in a list.
My code here is very much a mess, I know.

Android Recylcerview set Position of Item programmatically

i created a recylcerview for myChats. In this Recyclerview i can see a thumb, the last message and the name. if i send or receive a new message the item should go to first position, like in whatsapp. to receive the new message i created the following method:
private void getLastMSG(final String userId, final TextView lastMSG){
String userid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("Users").child(userid).child("connections").child("matches").child(userId);
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()){
String lastMsg = dataSnapshot.child("lastMsg").getValue().toString();
lastMSG.setText(lastMsg);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Now i want to set this item to first position but i dont know how
You need to call:
notifyItemMoved(oldPos, newPos) in your recyclerview adapter.
Note that you need to update your data model before that, in order to bind correct view in onBindViewHolder method.
Referece to adapter documentation.

Not getting correct data in ListView

I have an Arraylist checkOutBook containing keys. Its printing correctly to log message. However - i only want the books with matching the key of checkOutBook in the listview, But however im getting all books in database.
In the onPostResume method, Im checking the each key of the firebase realtime database with the checkOutBook Sting list, If only true, it should be added. But its getting true for each of the key in the database, however that is not present in the checkOutBook array. I dont know whats the issue here.
private ArrayList<String> checkOutBook;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
checkOutBook = new ArrayList<>();
Intent intent = getIntent();
checkOutBook =intent.getStringArrayListExtra("value");
Log.i("vvvv",checkOutBook.toString());
....
}
#Override
protected void onPostResume() {
super.onPostResume();
Log.i("yyy","onresume");
mAdapter.clear();
//to check if the university ID is already registered
mDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.i("GTA","HHH");
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
if(checkOutBook.contains(postSnapshot.getKey()));
{
Log.i("RRR","HHH");
BooksWithKey book = new BooksWithKey(postSnapshot.getKey(), postSnapshot.child("bookName").getValue().toString(), postSnapshot.child("author").getValue().toString(),
postSnapshot.child("copies").getValue().toString(), postSnapshot.child("publisher").getValue().toString(), postSnapshot.child("yearPublish").getValue().toString(), postSnapshot.child("callNumber").getValue().toString());
mAdapter.add(book);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w("TAG", "loadPost:onCancelled", databaseError.toException());
}
});
}
To solve this, combine this three lines of code:
private ArrayList<String> checkOutBook;
checkOutBook = new ArrayList<>();
checkOutBook =intent.getStringArrayListExtra("value");
in
ArrayList<String> checkOutBook = intent.getStringArrayListExtra("value");
Move this new line of code in your onDataChange() method right after this line: Log.i("GTA","HHH");.

Firebase - Trying to retrieve data of each user into a list

Each user in my app can send and get friend requests. When the user checks his friends requests, I want the app to go through each user who sent him a friend request and retrieve his information from the Realtime Database.
This is my code in order to accomplish this:
public void check_For_Friends_And_Requests(){
String loggedUser=SaveSharedPreference.getLoggedEmail(getApplicationContext());
final DatabaseReference mDatabase= FirebaseDatabase.getInstance().getReference();
final DatabaseReference userRef=mDatabase.child("Users").child(loggedUser);
userRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
final List<User> friendRequestsReceived_UserList=new ArrayList<>();
for (DataSnapshot postSnapshot: dataSnapshot.child("friend_requests_received").getChildren()) {
final String senderEmail=postSnapshot.getKey();
Toast.makeText(getApplicationContext(),
senderEmail, Toast.LENGTH_SHORT).show();
if (senderEmail!=null){
mDatabase.child("Users").child(senderEmail).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Toast.makeText(getApplicationContext(),
dataSnapshot.child("name").getValue(String.class), Toast.LENGTH_SHORT).show();
friendRequestsReceived_UserList.add(
new User(
senderEmail,
dataSnapshot.child("name").getValue(String.class),
dataSnapshot.child("level").getValue(Integer.class),
dataSnapshot.child("skill").getValue(Double.class)));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
UserListAdapter friendRequestsReceived_Adapter =
new UserListAdapter(getApplicationContext(),
R.layout.friend_requests_received_listview_row,
friendRequestsReceived_UserList);
friendRequestsReceived_ListView.setAdapter(friendRequestsReceived_Adapter);
}
else
connectionErrorGoToMain();
}
#Override
public void onCancelled(DatabaseError databaseError) {
connectionErrorGoToMain();
}
});
}
I have in this code 2 ValueEventListeners. I add the user information to the list in the inner one. The problem is that the list is empty at the end of this process.
I would like to fill a list view with this information using these lines:
UserListAdapter friendRequestsReceived_Adapter =
new UserListAdapter(getApplicationContext(),
R.layout.friend_requests_received_listview_row,
friendRequestsReceived_UserList);
friendRequestsReceived_ListView.setAdapter(friendRequestsReceived_Adapter);
When I put them inside the innner listener, it works fine, but I don't want to set the adapter for each user in the list, only after the for loop.
I'm attaching a screenshot with my database structure (I don't need to get all of the parameters):
The list is empty because you are declaring friendRequestsReceived_UserList outside the inner onDataChange() method. This is happening due the asynchronous behaviour of onDataChange() method which is called before you are adding those new objects to the list. So, in order to solve this, just move the declaration of the list inside the inner onDataChange() method like this:
if (senderEmail!=null){
mDatabase.child("Users").child(senderEmail).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final List<User> friendRequestsReceived_UserList=new ArrayList<>(); //Moved here
Toast.makeText(getApplicationContext(), dataSnapshot.child("name").getValue(String.class), Toast.LENGTH_SHORT).show();
friendRequestsReceived_UserList.add(
new User(
senderEmail,
dataSnapshot.child("name").getValue(String.class),
dataSnapshot.child("level").getValue(Integer.class),
dataSnapshot.child("skill").getValue(Double.class)));
UserListAdapter friendRequestsReceived_Adapter =
new UserListAdapter(getApplicationContext(), R.layout.friend_requests_received_listview_row, friendRequestsReceived_UserList);
friendRequestsReceived_ListView.setAdapter(friendRequestsReceived_Adapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
As you probably see, i set the adapter also inside the inner method. If you want to use that list outside the onDataChange() i suggest you reading my answer from this post.

Categories