My code works this way: when I'm on a post and I press "ok" on a AlertDialog.Builder then I go to:
private void borrar_post(){
Intent intent = new Intent(PostDetailActivity.this, MainActivity.class);
intent.putExtra("EXTRA_BORRAR_POST", mPostKey);
startActivity(intent);
}
I come back to main activity and I see if there's anything I need to delete:
String borrar = getIntent().getStringExtra("EXTRA_BORRAR_POST");
if (borrar != null) {
DatabaseReference mipost = FirebaseDatabase.getInstance().getReference();
mipost.child("user-posts").child(getUid()).child(borrar).removeValue();
mipost.child("posts").child(borrar).removeValue();
mipost.child("post-comments").child(borrar).removeValue();
}
I delete my post from the 3 places I have it on my firebase realtime database. The tree is:
It's possible I don't have anything on "post-comments", so I understand I might have a problem there (maybe I should check if there's something there before) but even when I comment that line, I keep having the same problem:
Sometimes everything gets deleted, sometimes nothing, most of the times only "user-posts" child gets deleted.
I know there's similar questions to mine, but I can't seem to find the mistake, any help?
Do you have security rules that limit write access as a function of the current value at a location? That might explain why some removes succeed and others fail.
To understand why some calls to removeValue() are failing, add a CompletionListener. You can define a debug listener as a field in your activity like this:
private DatabaseReference.CompletionListener mRemoveListener =
new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError error, DatabaseReference ref) {
if (error == null) {
Log.d(TAG, "Removed: " + ref);
// or you can use:
System.out.println("Removed: " + ref);
} else {
Log.e(TAG, "Remove of " + ref + " failed: " + error.getMessage());
}
}
};
Then add it to each of your calls to removeValue():
String borrar = getIntent().getStringExtra("EXTRA_BORRAR_POST");
if (borrar != null) {
// always good to log important values
Log.d(TAG, "borrar= " + borrar);
DatabaseReference mipost = FirebaseDatabase.getInstance().getReference();
mipost.child("user-posts").child(getUid()).child(borrar).removeValue(mRemoveListener);
mipost.child("posts").child(borrar).removeValue(mRemoveListener);
mipost.child("post-comments").child(borrar).removeValue(mRemoveListener);
}
Related
I need to insert lesson object to firebase, so I put here the onData change section of code.
First of all I get data snapshot and insert the lessons that I have in firebase, after that I scan the List of Lessons and check:
if the date and time exist in the firebase in any Lesson so I do something else I insert the lesson object to firebase .
The main problem is :
when I insert the details of the lesson and press add, the lesson enter to the firebase twice minimum, and if I try another insertion the program enter to infinite loop .
will be happy for any help !
ArrayList<Lesson> existLesson=new ArrayList<>();
List<String> keys = new ArrayList<>();
int counter=0;
public void getLessons(DataSnapshot dataSnapshot){
//insert the lessons to "existLesson" arrayList
for (DataSnapshot keyNode : dataSnapshot.getChildren()) {
keys.add(keyNode.getKey());
Lesson lesson = keyNode.getValue(Lesson.class);
existLesson.add(lesson);
Log.i(tag, "data : " + lesson.getSubject());
}//for
}
int flag=1;
#Override
public void addLesson(final String subject, final String topic, final String date, final String time) {
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
getLessons(dataSnapshot);
//Check if date and time is busy
for (Lesson lessonToCheck : existLesson) {
if (lessonToCheck.getDate().equals(date) && lessonToCheck.getTime().equals(time)) {
flag = 0;
} else {
flag = 1;
}
}//for
if (flag == 0) {
Toast.makeText(LessonDetails.this, "date exist", Toast.LENGTH_SHORT).show();
// Check empty lessons
nearestLessons(existLesson, date, time);
} else {
if (flag == 1) {
String id = mDatabase.push().getKey();
Lesson lesson = new Lesson(subject, topic, date, time, id); //create lesson
Toast.makeText(LessonDetails.this,
subject + " - " + topic + " - " + date + " - " + time, Toast.LENGTH_SHORT).show();
mDatabase.child(id).setValue(lesson);
} //add lesson to DB
} //else
Log.i(tag,"end");
} //onDataChange
When you call you're adding a listener to the data at. This listener will immediately read the data and call your onDataChange, and then continues to listen for updates to the data.
For each update to the data, it calls your onDataChange again. And since you're updating the data inside onDataChange, this ends in an endless loop of setValue->onDataChange->setValue->onDataChange->...
To fix this, you'd typically use addListenerForSingleValueEvent instead, as this only gets the value once and doesn't continue listening for changes.
So something like:
mDatabase.addForListenerValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
getLessons(dataSnapshot);
//Check if date and time is busy
for (Lesson lessonToCheck : existLesson) {
if (lessonToCheck.getDate().equals(date) && lessonToCheck.getTime().equals(time)) {
flag = 0;
} else {
flag = 1;
}
}//for
if (flag == 0) {
Toast.makeText(LessonDetails.this, "date exist", Toast.LENGTH_SHORT).show();
// Check empty lessons
nearestLessons(existLesson, date, time);
} else {
if (flag == 1) {
String id = mDatabase.push().getKey();
Lesson lesson = new Lesson(subject, topic, date, time, id); //create lesson
Toast.makeText(LessonDetails.this,
subject + " - " + topic + " - " + date + " - " + time, Toast.LENGTH_SHORT).show();
mDatabase.child(id).setValue(lesson);
} //add lesson to DB
} //else
Log.i(tag,"end");
} //onDataChange
})
Note that, since you're updating the data based on its current value, there's a chance that another user may be doing the same operation at almost the same time. If this can lead to conflicting updates in your use-case, consider using a transaction which combines the read and write from your code into a single (repeatable) operation.
I'm trying to grab subcollections using Firestore's .collectionGroup function however it doesn't seem to be firing at all. It should be returning at least one document found since its status is "Accepted" however when I run debug, it seems to skip over the function and doesn't return any kind of error or any of my logs.
When I run it normally, not in debug mode, it returns "onComplete: found no book reservations for this book"
I also was not prompted initially to create the composite index as is normally done with a singular index field when a new collection group query is first executed so I've attempted to make my own although I'm pretty sure it's not working correctly.
Any help or guidance is appreciated on how to solve this.
my firebase db setup is:
Collection:"Books"
-->subcollection:"bookPendingRequests"
here is a debug screen show to show that it is reading the correct bookFirebaseId
here is a markup of the firebase set up to illustrate what it should be reading.
here is my collectionGroup index:
Here is my code which is set to start at onCreate()
private void loadBookReservationDates() {
getIncomingIntent();
bookPendingRequests = firestoreDB.collectionGroup("bookPendingRequests")
.whereEqualTo("bookRequestParentId", bookFirebaseIdString)
.whereEqualTo("bookReservationStatus", "Accepted");
bookPendingRequests.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.getResult().size() < 0) {
for (int i = 0; i < task.getResult().size(); i++) {
String dates = task.getResult().getDocuments().get(i).get("bookReservationDates").toString();
Log.d(TAG, "onComplete: dates: " + dates);
}
} else {
Log.d(TAG, "onComplete: found no book reservations for this book");
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "onFailure() returned: " + e);
}
});
}
I'm getting errors when trying to get a player's scores across all leaderboards, in order to store locally.
My Android game is designed to be played offline, so I store high score and lifetime scores locally for each game mode.
Now I am working on adding leaderboards to my game. During the game play, I periodically post these scores to the leaderboards. So far so good.
Naturally, I need to cover the scenario where a user either uninstalls my game or moves to a new device, etc. In that scenario, when they sign in again to Google, I need to update the scores that are on leaderboards back into my local version.
Here's where I seem to get 26504 - NETWORK_ERROR_NO_DATA when I make the 4th call.
I have tried to wait until the 3rd call is complete and then fire the 4th one but I still get the same error. So I haven't even been able to add code for the 5-6 additional scores I will still need to retrieve.
getScoresFromLeaderboards(GAME_MODE_FIXED_QUESTIONS, SCORE_HIGH);
getScoresFromLeaderboards(GAME_MODE_FIXED_QUESTIONS, SCORE_LIFETIME);
getScoresFromLeaderboards(GAME_MODE_RAPID_FIRE, SCORE_HIGH);
getScoresFromLeaderboards(GAME_MODE_RAPID_FIRE, SCORE_LIFETIME);
Then...
public long getScoresFromLeaderboards(final String gameMode, final String scoreType) {
final int scoreHigh = 0, scoreLifetime = 0;
String leaderboardID = getLeaderboardForGameMode(gameMode, scoreType);
mLeaderboardsClient.loadCurrentPlayerLeaderboardScore(leaderboardID, LeaderboardVariant.TIME_SPAN_ALL_TIME, LeaderboardVariant.COLLECTION_PUBLIC )
.addOnCompleteListener(new OnCompleteListener<AnnotatedData<LeaderboardScore>>() {
#Override
public void onComplete(#NonNull Task<AnnotatedData<LeaderboardScore>> task) {
if (task.isSuccessful()) {
AnnotatedData<LeaderboardScore> lbs = task.getResult();
long i = lbs.get().getRawScore();
// Do something to store the score locally
}
else{
Log.Error("Error", "Retrieval failed");
}
}
I would suggest another approach to using "loadCurrentPlayerLeaderboardScore" which has the 3 query limit
Use loadPlayerCenteredScores instead . Limit the scores to 1. The resultant buffer will return only the player score. You are now in the user quota of 500 requests instead of 3.
long limitResultsTo = 1;
String PlayerID = "-1"; // set this from playersClient.getCurrentPlayer() ->task-> getPlayerId()
String leaderboardID = getString(R.string.leaderboard_name); // or string of ID
Games.getLeaderboardsClient(this, GoogleSignIn.getLastSignedInAccount(this))
.loadPlayerCenteredScores(leaderboardName, LeaderboardVariant.TIME_SPAN_ALL_TIME, LeaderboardVariant.COLLECTION_PUBLIC, limitResultsTo)
.addOnSuccessListener(new OnSuccessListener<AnnotatedData<LeaderboardsClient.LeaderboardScores>>() {
#Override
public void onSuccess(AnnotatedData<LeaderboardsClient.LeaderboardScores> leaderboardScoreAnnotatedData) { // big ups Danoli3.com for the fix for loadCurrentPlayerLeaderboardScore
LeaderboardsClient.LeaderboardScores scoresResult = leaderboardScoreAnnotatedData.get();
LeaderboardScore scoreResult = (scoresResult != null ? scoresResult.getScores().get(0) : null);
long score = 0;
if(scoreResult != null && scoreResult.getScoreHolder() != null &&
PlayerID.equals(scoreResult.getScoreHolder().getPlayerId())) // else if the player has 0 score it will retrieve top player
score = scoreResult.getRawScore();
// use the score in your own code here
// Log.i(TAG, "loadPlayerCenteredScores:" + leaderboardID + " score:" + score);
leaderboardScoreAnnotatedData = null;
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.e(TAG, "Failure:loadPlayerCenteredScores GPG:Leader:" + leaderboardID + " Ex:" + e.getMessage());
}
Firebase real time database could not save EditText string or String test_case = "this is test case message" in setValue,but it is successfully saving databaseReference.child(id_key).setValue("given string text");
final Text_Strings text_strings = new Text_Strings(user_name, id_key, title_m, question_m);
// to save elements database referefernce called
databaseReference.child(id_key).setValue(text_strings, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (databaseError != null) {
Log.d("Data could not be saved ", databaseError.getMessage());
} else {
// it is always saying data
Toast.makeText(getContext(), "Data saved succcessfully ", Toast.LENGTH_SHORT).show();
}
}
});
// to save id_key for test cases
databaseReference.child(id_key).setValue(test_case);
/// to save string elements for test cases
databaseReference.child(id_key).setValue("given string text");
}
});
// return view
return view;
}
}
;
Check whether you are in test mode or applied read write rules correctly .
You can refer to Docs for Read and Write data in Firebase Realtime Database : https://firebase.google.com/docs/database/android/read-and-write
Please paste you complete activity code to better understand your problem.
I want to fill an ArrayList with the results I get back from a Parse query. When I get the results I add them to the ArrayList and print the ArrayList size to the console to make sure the results are added, which is succesful, but when I return the ArrayList it's empty. Can anyone explain to me why this happens?
public ArrayList<ParseObject>findAllGroupByUserId(ParseUser userId){
//TODO hier uit db halen alle groupen van user
final ArrayList<ParseObject> groups = new ArrayList<>();
ParseQuery<Group_user> query = ParseQuery.getQuery("Group_user");
query.whereEqualTo("user_id", userId);
query.findInBackground(new FindCallback<Group_user>() {
#Override
public void done(List<Group_user> objects, ParseException e) {
if (e == null) {
for (Group_user group : objects) {
Log.e("SUCCESS", group.getObjectId() + " , " + group.getGroup_id().getObjectId());
ParseObject g = new Group();
groups.add(g);
}
System.out.println(groups.size() + " :Done method"); //THIS RETURNS 2
} else {
Log.e("ERROR", "message: " + e);
}
Log.e("SUCCESS", "we have " + groups.size() + " results");
}
});
System.out.println(groups.size() + " :return"); // THIS RETURNS 0
return groups;
}
Because findInBackground() runs asynchronously on a different thread. You need to execute your remaining logic from the done() call back to get the populated array.
Think of it like this:
Thread 1 -> invokes findInBackground() -> thread one is running -----------> group is empty until Thread 2 finishes
Thread 2 spawned -> reaches out to server and gets query results -> invokes done call back on Thread 1 (now you have the data ready)
So I'm assuming that Group_user is a subclass of ParseObject that you've already defined. Since the findInBackground is async, you should change logic of the calling of the function to async too. Instead of returning list of objects like you were before, do all the logic in the done function of the query, no need to return.
public void findAllGroupByUserId(ParseUser userId) {
ParseQuery<Group_user> query = ParseQuery.getQuery("Group_user");
query.whereEqualTo("user_id", userId);
query.findInBackground(new FindCallback<Group_user>() {
#Override
public void done(List<Group_user> groups, ParseException e) {
if (e == null && groups != null) {
for (Group_user group : groups) {
// perform all logic here
}
} else {
Log.e("Find Callback", "Oh no! Query failed!");
}
}
});
}