Issue:
Im sure im missing something super easy, but I am not able to figure it out.
I have an if statement for a recycler view which basically says to pull in all data of the current user and put it in a recycler view.
What I want to happen:
If there is no data for the current user (so if the recycler view is empty), then display a particular image.
What's currently happening:
The image continues to display, even when the current user's data (recycler view) is displayed.
The image is displaying over the recycler view.
I need the image to be "GONE", if the recycler view is displayed with the current user data.
What I Believe The Issue Is:
I believe this is happening because the system is thinking that because there are other users that are not the current users that exist in the database, then display the image. However, im trying to figure out how to only display the image, if there is no data for the current user. Sorry if I didn't explain this correctly. Let me know if you have any questions. Thank you so much for the help!
Additional Notes
Let me know if you need to see the xml but I have already checked and the image is set to gone on the xml.
private void fetchInviteList() {
String currentUser;
currentUser = mAuth.getUid();
DatabaseReference notificationInviteRef = FirebaseDatabase.getInstance().getReference().child(Strings.InvitesReference);
notificationInviteRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot1) {
ArrayList<Model_Notifications_Invite> model_notifications_invites = new ArrayList<>();
for (DataSnapshot snapshot2:snapshot1.getChildren()) {
for (DataSnapshot snapshot3:snapshot2.getChildren()) {
String notificationPath = snapshot3.child("inviteeUserId").getValue(String.class);
if(notificationPath.equals(currentUser)){
Model_Notifications_Invite invites = snapshot3.getValue(Model_Notifications_Invite.class);
model_notifications_invites.add(invites);
} else if (!notificationPath.equals(currentUser)){
happyImage.setVisibility(View.VISIBLE);
}
}
}
myNotificationsAdapter.updateNotificationsList(model_notifications_invites);
}
#Override
public void onCancelled(DatabaseError databaseError) {
//throw databaseError.toException();
}
});
}
JSON
The first node is like the group id.
The child directly underneath that, is the current user id.
There is another child with the same user id thats called - "inviteeUserId."
"Invites": {
"-NAHHVbARBzsi0mCk4j9": {
"DrpEs5nJH4RVVdOXlgVB1EJQQDz1": {
"inviteDate": "August 25",
"inviteTime": "02:39:33 PM",
"inviteeUserId": "DrpEs5nJH4RVVdOXlgVB1EJQQDz1",
"inviterUserId": "iJ8ZuvIPtEd4pdKiychcnskgyHU2",
"bookId": "-NAHHVbARBzsi0mCk4j9"
},
"oWaE8TWfyAalpWyD9oUdV80ocsw2": {
"inviteDate": "August 25",
"inviteTime": "04:08:28 PM",
"inviteeUserId": "oWaE8TWfyAalpWyD9oUdV80ocsw2",
"inviterUserId": "iJ8ZuvIPtEd4pdKiychcnskgyHU2",
"bookId": "-NAHHVbARBzsi0mCk4j9"
}
}
Related
I've searched everywhere with no luck. I want to query Firestore to get all users WHERE type is admin. Something like:
SELECT * FROM users WHERE type=admin
but only when the property total is changing. If I'm using:
users.whereEqualTo("type", "admin").addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot snapshots, #Nullable FirebaseFirestoreException e) {
for (DocumentChange dc : snapshots.getDocumentChanges()) {
switch (dc.getType()) {
case ADDED:
//Not trigger
break;
case MODIFIED:
//Trigger
break;
case REMOVED:
//
break;
}
}
}
});
The case ADDED is triggered first time when I query and when the total is changed case MODIFIED is triggered again (this is what is want). I want only changes and not the all initial data, I don't need it. How to get it?
Please help me, is the last part of my project. How to skip is case ADDED?
When you are listening for changes in Cloud Firestore for realtime changes, using Firestore Query's addSnapshotListener() method, it:
Starts listening to this query.
Which basically means that first time you attach the listener, you get all documents that correspond to that particular query. Furthermore, everytime a property within a document changes, you are notified according to that change. Obviously, this is happening only if the listener remains active and is not removed.
Unfortunately, Firestore listeners don't work that way, so you cannot skip that "case ADDED". What you can do instead, is to add add under each user object a Date property (this is how you can add it) and query your database on client, according to this new property, for all documents that have changed since a previous time.
According to Nick Cardoso's comment, for future visitors that might ask why this behaviour happens, is because the reason he mentioned in his comment. I also recommend see Doug Stevenson's answer from this post, for a better understanding.
There is an option to check if the querySnapshot is from a cache, changes return false
if(querySnapshot.getMetadata().isFromCache()) return
Here is a solution working for me:
use
AtomicBoolean isFirstListener = new AtomicBoolean(true);
and then on event method
if (isFirstListener.get()) {
isFirstListener.set(false);
//TODO Handle the entire list.
return;
}
Here is a sample code from my project:
final AtomicBoolean isFirstListener = new AtomicBoolean(true);
mDb.collection("conversation_log").document(room_id).collection("messages").orderBy("sent_at")
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot value2, #Nullable FirebaseFirestoreException e) {
if (isFirstListener.get()) {
isFirstListener.set(false);
//TODO Handle the entire list.
return;
}
}
});
reference: answer
Found a work around for this given use case, it is possible to skip the initial data and get only updates. The workaround is including a server timestamp in your structure and build your query to fetch only the data that has timestamp greater than the current time.
val ref = db.collection("Messages").document("Client_ID")
.collection("Private")
.orderBy("timestamp")
.whereGreaterThan("timestamp",Calendar.getInstance().time)
//Add snapshot listener
ref.addSnapshotListener { snapshot , e ->
if (snapshot != null) {
Log.d("TAG", "Current data: ${snapshot.documents.size}")
for(document in snapshot.documents){
Log.e("Document Data",document.data.toString())
}
}
}
So, the query won't return any data in the initial build, but will listen to the document changes. As soon as timestamp of a document changes, you'll be notified about that change. Then you can check if the data exists in your list (if you're looking for modifications, or if it's a new document added)
Just update timestamp of the document when your write any changes. As shown below :
val message = hashMapOf<String,Any>(
"timestamp" to FieldValue.serverTimestamp(),
"id" to userId,
"data" to data,
)
db.collection("Messages").document("Client_ID")
.collection("Private")
.document().set(message)
In an android app I have made, the home page shows a list a games in a listview, each row being custom made to include a textview and an imageview and some buttons. The imageview's src comes from downloading th image from my google cloud firebase, and the functionality of this works well, but when the listview is scrolled through there is an issue. The images seem to be unloaded when scrolled away from, which causes a bit of lag when they are reloaded once scrolled back to. I imagine this is built in to prevent a listview from loading many high resolution images and keeping them loaded, but for my list, keeping the images loaded won't be a problem. Is there a way I can turn this off and just keep the images loaded? Here is a video of the problem:
https://www.youtube.com/watch?v=FYYJWZcvBy4&feature=youtu.be
here is the code of the listview and getting the image from firebase:
public void showData(DataSnapshot dataSnapshot){
TextView toolbarTitle = findViewById(R.id.toolbarTitle);
toolbarTitle.setText("Popular Games");
ArrayList<GameInformation> PopularGames = new ArrayList<>();
Iterator<DataSnapshot> items = dataSnapshot.child("Games").getChildren().iterator();
PopularGames.clear();
while(items.hasNext()){
GameInformation game = new GameInformation();
DataSnapshot item = items.next();
game.setGameName(item.child("Name").getValue(String.class));
game.setActiveLobbies(Integer.parseInt(item.child("Live Lobbies").getValue().toString()));
game.setPicturePath(item.child("FilePathName").getValue().toString());
Iterator<DataSnapshot> itemsDeep1 = item.child("consoles").getChildren().iterator();
while(itemsDeep1.hasNext()){
DataSnapshot itemDeep = itemsDeep1.next();
game.setConsoles(itemDeep.getValue(String.class));
}
Iterator<DataSnapshot> itemsDeep2 = item.child("genres").getChildren().iterator();
while(itemsDeep2.hasNext()){
DataSnapshot itemDeep = itemsDeep2.next();
game.setGenres(itemDeep.getValue(String.class));
}
if (game.getActiveLobbies() == 1){
PopularGames.add(game);
}
}
Log.d(TAG, "Hello Here" + PopularGames.size());
ArrayAdapter adapter = new CustomListAdapter(this, PopularGames);
mListView.setAdapter(adapter);
}
getting the image:
storageRef.child("Games/"+singleGame.getPicturePath()+".jpg").getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
Glide.with(getContext()).load(uri).into(gameImageID);
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// Handle any errors
}
});
The delay you're facing is because there are 2 operations (each one takes a few secs to complete) being performed when you get each image:
The image is read from Firebase Storage using getDownloadUrl()
The image is displayed with Glide
I recommend that when you store the images, save their Download URL to the database instead of saving their path. This way, you'd no longer need to call for getDownloadURL(). You'd simply load the image with a single operation:
Glide.with(getContext()).load(singleGame.getPicturePath()).into(gameImageID);
Not related to the question:
You can simplify your code by using a forEach loop (instead of while) in your Iterators:
PopularGames.clear();
for(DataSnapshot item : dataSnapshot.child("Games").getChildren()){
GameInformation game = new GameInformation();
game.setGameName(item.child("Name").getValue(String.class));
game.setActiveLobbies(Integer.parseInt(item.child("Live Lobbies").getValue().toString()));
game.setPicturePath(item.child("FilePathName").getValue().toString());
for(DataSnapshot itemDeep: item.child("consoles").getChildren()){
game.setConsoles(itemDeep.getValue(String.class));
}
for(DataSnapshot itemDeep: item.child("genres").getChildren()){
game.setGenres(itemDeep.getValue(String.class));
}
if (game.getActiveLobbies() == 1){
PopularGames.add(game);
}
}
Log.d(TAG, "Hello Here" + PopularGames.size());
I've a realtime database of only 200 B and my app has approx. 500 active users.
In the app I query the database to add data to some arraylist:
public class FirebaseApplication extends Application {
...
#Override
public void onCreate() {
super.onCreate();
...
//reference to the root of the cloud database
mDatabase = FirebaseDatabase.getInstance().getReference();
mNamesPoloSubRoot = mDatabase.child("PoloniexNames");
//listen for change in the cloud. If there is some additional item,
//add it to the list populated from hard coded items.
mNamesPoloSubRoot.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
GenericTypeIndicator<List<String>> gt = new GenericTypeIndicator<List<String>>() {};
List names = snapshot.getValue(gt);
if( names == null ) {
//Log.v(LOG_TAG_FIREBASE,"No extra coins");
}
else {
// Log.v(LOG_TAG_FIREBASE,"extra coins!" );
additionalNamesPolo = new ArrayList<String>(names);
//attach the new coins at the bottom of the hard coded list
NamesPoloniexA.addAll(additionalNamesPolo);
}
}
#Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.e(LOG_TAG_FIREBASE, "Failed to read value.", error.toException());
}
});
...
}
}
I've 9 of such listeners. The data consumptions is approx. 100 MB every day!
At this rate I will soon hit the 10GB/month limit. If I'm correct this data should be loaded every time a user open the app and every time I change remotely the data from server (this happens very rarely, like 2 times in a month). If opening the app loads, let's say, 1 KB, then this means that app should have been opened more than 100.000 times a day, and this it's not true by at least an order of magnitude.
My database is something like:
{
"PoloniexAbbreviations" : [ "LGD", "PTOY", "PTOY" ],
"PoloniexCurrencyPairs" : [ "ETH_LGD", "BTC_PTOY", "ETH_PTOY" ],
"PoloniexNames" : [ "Legends", "Patientory", "Patientory" ]
}
Notice that many of the children called by the app are very often null (since there are no data to add remotely to the app), so the entire database can even be empty, but some data seems to be downloaded anyway (the size of the database, even if null, is not zero).
What I'm doing wrong? Or it's an issue of Firebase?
I'm having a loading screen where I fetch data belonging to the logged in user. Among this data is their profile images (three of them).
What I want to do is to cache the images when I download them with their URL in Loading screen. Then in the next activity I want to access the cached images to display them in that activity. So basically load all images in one activity to be used in another one. This way (I figured) I won't have to make a http request everytime user enters the activity where their profile image is shown. Which gives a better user experience.
So it's the matter of getting the images from cache to screen that I can't work out, cause I think I've loaded them into cache correctly, seen below.
Here's my current method in Loading Activity:
Backendless.Data.of(UserFileMapping.class).find(new AsyncCallback<BackendlessCollection<UserFileMapping>>() {
#Override
public void handleResponse(BackendlessCollection<UserFileMapping> response) {
Iterator<UserFileMapping> iterator = response.getCurrentPage().iterator();
while(iterator.hasNext()){
UserFileMapping fileMapping = iterator.next();
String profile1 = fileMapping.profile_url;
String profile2 = fileMapping.profile_url_2;
String profile3 = fileMapping.profile_url_3;
Picasso.with(getApplicationContext())
.load(profile1)
.fetch(new Callback() {
#Override
public void onSuccess() {
continue_button.setVisibility(View.VISIBLE);
loading_text.setText("Done!");
}
#Override
public void onError() {
//Make toast
}
});
}
}
#Override
public void handleFault(BackendlessFault fault) {
System.out.println("FAULT:" + fault.getCode());
}
});
It's the issue with downloading all three images at once as well, but that isn't as important as the caching question. If you have a good idea on this in addition to the cache issue I would gladly like to hear it too.
I would like to set the priority of a child using the server time stamp provided by Firebase, ServerValue.TIMESTAMP:
mFirebaseref.child(userid).setPriority(ServerValue.TIMESTAMP);
But my case is inverse. I want to set negative ServerValue.TIMESTAMP to move my child to the top based on time. Is it possible to do that in Firebase without using the local time stamp System.CurrentTimeInMillis()?
I would like to do something like this:
mFirebaseref.child(userid).setPriority(-ServerValue.TIMESTAMP);
On the client side, ServerValue.TIMESTAMP is an object structured like this: {.sv: "timestamp"}
So, as you know, you can't easily do what you wanted. However, there may be a different solution. If, for example, you wanted the five most recent entries, you could still set the priority by ServerValue.TIMESTAMP:
mFirebaseref.child(userid).setPriority(ServerValue.TIMESTAMP);
And then use the limitToLast() method:
Query queryRef = mFirebaseref.limitToLast(5);
To get the five most recent entries.
Also, this may help: Display posts in descending posted order
You are basically asking how to get negative server timestamp and it should work offline. I found a way, there is a hidden field you can use. A snippet from documentation:
Firebase offsetRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/.info/serverTimeOffset");
offsetRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
double offset = snapshot.getValue(Double.class);
double estimatedServerTimeMs = System.currentTimeMillis() + offset;
}
#Override
public void onCancelled(FirebaseError error) {
System.err.println("Listener was cancelled");
}
});