I'm trying to save GeoLocations with GeoFire in Firebase, the key of the GeoLocation must be the server timestamp. So the data model is as follows:
{
"geofire" : {
"1515755844766" : {
".priority" : "ez53wur36x",
"g" : "ez53wur36x",
"l" : [ 39.66227391798104, -6.372992321848869 ]
},
"1515755844962" : {
".priority" : "ez53wur36x",
"g" : "ez53wur36x",
"l" : [ 39.66227391798104, -6.372992321848869 ]
},
"1515755851938" : {
".priority" : "s6xhjeruen",
"g" : "s6xhjeruen",
"l" : [ 14.78428771229891, 21.346221640706066 ]
},
"1515755852148" : {
".priority" : "s6xhjeruen",
"g" : "s6xhjeruen",
"l" : [ 14.78428771229891, 21.346221640706066 ]
}
},
"serverTime" : 1515755852148
}
I just executed the following code twice to get those values (four values), so it saves key (timestamp) twice for each .setLocation(), with a small diference in miliseconds and seconds.
Why could this be happening?
//Get reference to Firebase DB
dbRef = FirebaseDatabase.getInstance().getReference();
// Get reference to Geofire inside FIrebaseDB
dbRefGeofire = FirebaseDatabase.getInstance().getReference("geofire");
geoFire = new GeoFire(dbRefGeofire);
dbRef.child("serverTime").setValue(ServerValue.TIMESTAMP); //Executes TIMESTAMP function in firebase server and stores that value
dbRef.child("serverTime").addValueEventListener(new ValueEventListener() { //Gets TIMESTAMP value from the server
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
serverTime = String.valueOf(dataSnapshot.getValue());
geoFire.setLocation(serverTime, new GeoLocation(latLng.latitude,latLng.longitude), new GeoFire.CompletionListener() {
#Override //Save geolocation with timestamp in seconds
public void onComplete(String key, DatabaseError error) {
if (error != null) {
Log.v("Informe","There was an error saving the location to GeoFire: " + error);
} else {
Log.v("Informe","Location saved on server successfully!");
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
To solve this, please use the following code:
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String serverTime = String.valueOf(dataSnapshot.getValue(Long.class));
geoFire.setLocation(serverTime, new GeoLocation(latLng.latitude,latLng.longitude), new GeoFire.CompletionListener() {
#Override
public void onComplete(String key, DatabaseError error) {
if (error != null) {
Log.v("Informe","There was an error saving the location to GeoFire: " + error);
} else {
Log.v("Informe","Location saved on server successfully!");
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
dbRef.child("serverTime").addListenerForSingleValueEvent(eventListener);
addListenerForSingleValueEvent solves your problem.
The ValueEventListener excuted twice, if you will set breakpoint at serverTime = String.valueOf(dataSnapshot.getValue());
you will see it clearly,
Why that's happen?
because your dbRef.child("serverTime").setValue(ServerValue.TIMESTAMP); is async,
The application continue to run and set a new valueEventListener,
which will call immediately without any data in dataSnapshot
and then, when you async 'setValue' function will finish and 'onDataChange' will call again (because that the value has been changed)
You can just add 'datasnapshout.isExist()' to solve it,
or add 'onCompleteListener' to your setValue line like this:
dbRef.setValue(ServerValue.TIMESTAMP, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference dataRef) {
dbRef.child("serverTime").addValueEventListener(new ValueEventListener() { //Gets TIMESTAMP value from the server
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
serverTime = String.valueOf(dataSnapshot.getValue());
geoFire.setLocation(serverTime, new GeoLocation(latLng.latitude,latLng.longitude), new GeoFire.CompletionListener() {
#Override //Save geolocation with timestamp in seconds
public void onComplete(String key, DatabaseError error) {
if (error != null) {
Log.v("Informe","There was an error saving the location to GeoFire: " + error);
} else {
Log.v("Informe","Location saved on server successfully!");
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
});
Related
This question already has answers here:
How to save users score in firebase and retrieve it in real-time in Android studio
(3 answers)
Closed 2 years ago.
I am trying to make an application on Android, I am a beginner.
I try to increment the "stock" of a product, but my following code is not reliable. I had connection problems on the device and the "stock" did not increase correctly. How can I execute it as a transaction?. I cannot find extensive documentation.
final HashMap<String, BigDecimal> detalle = new HashMap<String, BigDecimal>();
Query query = mDatabase.child("COMPRAS").child(key).child("COMPRASPRODUCTO");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()){
CompraProducto producto = ds.getValue(CompraProducto.class);
detalle.put(producto.getId(),new BigDecimal(producto.getCantidad()));
}
for (String key : detalle.keySet()) {
Query queryp = mDatabase.child("PRODUCTO").child(key);
queryp.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot data) {
Producto p = data.getValue(Producto.class);
try{
stockOriginal = new BigDecimal(p.getStock());
mProductoProvider = new productoProvider();
mProductoProvider.refreshStock(p.getCodigo(), stockOriginal.add(detalle.get(p.getCodigo())).toString()).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
}
});
}catch(Exception e){
Toast.makeText(comprarProducto.this, "Producto " + data.getKey() + " no existe.", Toast.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getCode());
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
Thank you.
I am using updateChildren and ServerValue.increment
Using these paths, you can perform simultaneous updates to multiple locations in the JSON tree with a single call to updateChildren(), such as how this example creates the new post in both locations. Simultaneous updates made this way are atomic: either all updates succeed or all updates fail.
final Map<String, Object> productUpdates = new HashMap<>();
Query query = mDatabase.child("COMPRAS").child(key).child("COMPRASPRODUCTO");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()){
System.out.println("---" + ds.getKey());
CompraProducto producto = ds.getValue(CompraProducto.class);
productUpdates.put("/PRODUCTO/" + ds.getKey() +"/stock", ServerValue.increment(producto.getCantidad()));
}
System.out.println("==============");
if(!productUpdates.isEmpty()) {
mDatabase.updateChildren(productUpdates, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (databaseError == null) {
mCompraProvider.actualizar(key, inputProveedor.getText().toString(), inputFecha.getText().toString(), "PENDIENTE").addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(comprarProducto.this, "Compra del proveedor " + inputProveedor.getText().toString() + " guardada correctamente.", Toast.LENGTH_SHORT).show();
}
});
System.out.println("onComplete: success");
} else {
System.out.println("onComplete: fail" + databaseError.toException());
}
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
Here is my db format:
{
"classes": {
"class1": {
"name":"C1"
},
"class2": {
"name":"C2"
}
},
"students": {
"student1": {
"name":"S1"
},
"student2": {
"name":"S2"
}
},
"classes_enrollments": {
"class1": {
"student1": true,
"student2": true
},
"class2": {
"student1": true
}
},
"students_enrollments": {
"student1": {
"class1": true,
"class2": true
},
"student2": {
"class1": true
}
}
}
I want to get data like : class1 named C1, has 2 students S1 and S2
I tried this way
database.child("classes_enrolments").addValueEventListener(eventListener);
but the output is just the data of classes_enrolments node: {class2={student1=true}, class1={student2=true, student1=true}}
How to achieve it, please?
Thank you
To solve this, please use the cpde below:
String class1 = "class1";
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference nameRef = rootRef.child("classes").child(class1).child("name");
ValueEventListener nameValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot nameDataSnapshot) {
String className = nameDataSnapshot.getValue(String.class);
Log.d(TAG, class1 + " named " + className);
DatabaseReference class1Ref = rootRef.child("classes_enrollments").child(class1);
ValueEventListener class1EventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
long numberOfStudent = dataSnapshot.getChildrenCount();
Log.d(TAG, "has " + numberOfStudent + " students");
for(DataSnapshot studentDataSnapshot : dataSnapshot.getChildren()) {
String student = studentDataSnapshot.getKey();
DatabaseReference studentsRef = rootRef.child("students");
DatabaseReference studentRef = studentsRef.child(student);
ValueEventListener studentsValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String name = ds.child("name").getValue(String.class);
Log.d(TAG, name);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
}
};
studentRef.addListenerForSingleValueEvent(studentsValueEventListener);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
}
};
class1Ref.addListenerForSingleValueEvent(class1EventListener);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
}
};
nameRef.addListenerForSingleValueEvent(nameValueEventListener);
In order to solve this, you should first get the name of the class, the query to get the number of student and in the end to get the name of the students. The result in the logcat will be:
class1 named C1
has 2 students
S1
S2
I am trying to search a "username" in firebase database but it always returns the else statement
mDatabaseref = FirebaseDatabase.getInstance().getReference("user_info");
mDatabaseref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.child("username").child(usernamedatabasesend).exists())
{
Log.i("USERINFO","USER EXISTS");
}
else
{
Log.i("USERINFO","USER DOES NOT EXISTS");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
mDatabaseref = FirebaseDatabase.getInstance().getReference("user_info");
mDatabaseref.push().setValue(uic);
the usernamedatabasesend is the Edittext value to send it to the database to check if that same value the user is entering is existing on the db or not
The Database node is like this
"user_info" : {
"-L-7QPKXFyoN-GlPxTTN" : {
"email" : "",
"name" : "",
"password" : "",
"username" : "ujjwalbassi"
},
"-L-7QPMyzXCqpWT0YLPM" : {
"email" : "",
"name" : "",
"password" : "",
"username" : "ujjwalbassi"
}
}
****UPDATE*********
This is the new code
mDatabaseref.orderByChild("username").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot dataSnapshot1: dataSnapshot.getChildren())
{
userInfo userinfoclass = dataSnapshot1.getValue(userInfo.class);
String usernamegotunamn = userinfoclass.getUsername().toString();
if(usernamegotunamn.equals(usernamedatabasesend))
{
Log.i("YESONO","USEREXISTS"+"\n"+usernamegotunamn+"\n"+usernamedatabasesend);
}
else {
mDatabaseref.push().setValue(uic);
Log.i("YESONO", "USERDOESNOTEXIST");
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
The if else is working but if the "IF" is true then else works with it too. but it shows that if the user exists or not.
Try this.
mDatabaseref.orderByChild("username").equalTo("ujjwalbassi").addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//data will be available on dataSnapshot.getValue();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "getUser:onCancelled", databaseError.toException());
}
});
Reference : How to Search for data in Firebase Android
To check if the user exists by looping, you'll need to loop through each DataSnapshot and see if the username matches. To do this you need to, first, get a DataSnapshot of all the users, and then loop through each one:
mDatabaseref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot user : dataSnapshot){
if(user.child("username").getValue(String.class).equals(usernamedatabasesend)){
//The username matches!
Log.i("USERINFO","USER EXISTS");
return
}
}
}
#Override public void onCancelled(DatabaseError databaseError) {}
});
This isn't the best practice (Will get slow if you have a ton of users) but it is definitely a working solution. I recommend using #lovekush-vishwakarma's answer as a faster solution.
You cannot use exists() method to check whether a value exists or not. If you want to use exists() method, then you should consider change your database structure a little bit. Instead of having as a unique identifier the id that is generated by the push() method, just use the user name. Your database structure should look like this:
"user_info" : {
"ujjwalbassi" : {
"email" : "",
"name" : "",
"password" : "",
},
"anotherUserName" : {
"email" : "",
"name" : "",
"password" : "",
}
}
The important thing here is, when you add a new user to the database to check the userame for uniqueness. To verify if a user exists, you can use the following code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userNameRef = rootRef.child("user_info").child(usernamedatabasesend);
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()) {
//do something
} else {
//do something else
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
userNameRef.addListenerForSingleValueEvent(eventListener);
Another approach will be to filter the database after usernamedatabasesend and get all the users with the same user name. This is not a good practice, to have users in your database which have the same user name. If you want to go with this, you can use orderByChild() method in a query like this:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Query userNameQuery = rootRef.child("user_info").orderByChild("username").equalTo(usernamedatabasesend);
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()) {
//do something
} else {
//do something else
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
userNameQuery.addListenerForSingleValueEvent(eventListener);
If the user name contains a dot ., then you need to encode it in order to use it as a key in the database. To encode the user name please use the following mothod:
static String encodeUserName(String userName) {
return userName.replace(".", ",");
}
And to get it back, please use the following code:
static String decodeUserName(String userName) {
return userName.replace(",", ".");
}
I created a helper class including the functions and processes I would like to perform over my data in FirebaseDatabase.
The following function was meant to get all Posts in posts Node in fireabseDataBase:
public static Jobs getAllJobs() {
Posts posts= new Posts();
postsDBRef= FirebaseDatabase.getInstance().getReference("posts").getRef();
postsDBRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot jobSnapshot: dataSnapshot.getChildren()) {
// TODO: handle the post
String key = jobSnapshot.getKey();
//here
Post post= jobSnapshot.child(key).getValue(Post.class);
// and here
posts.add(post);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.i(getClass().getName().toString(), ": " + databaseError.getMessage());
}
});
return posts;
}
in the onDataChange function I am trying to loop over the data and retrieve it, although while logging I do get data but the post object keeps giving Null
Here's How the data look like in firebase DataBase
{
"posts" : {
"-Kr9-ii-ArpC3fLuuGnb" : {
"address" : "sfhhfg",
"applied" : false,
"currency" : "le",
"description" : "ehdhyf",
"latitude" : 0,
"longitude" : 0,
"noOfWorkers" : 2,
"rating" : 0,
"reported" : false,
"salary" : "1250",
"title" : "rgchu",
"userId" : 0
},
"-Kr94BDkdEZrmxmjxv_Z" : {
/../
},
"-Kr9Dz3S0BQEYv4VO2l3" : {
/../
},
"-Kr9XqDPUvCRcv0lwFy_" : {
/../
}
}
}
and here’s what am getting from the Debugger in a single loop
DataSnapshot { key = -Kr9-ii-ArpC3fLuuGnb, value = {address=sfhhfg,
rating=0, title=rgchu, reported=false, description=ehdhyf, userId=0,
longitude=0, noOfWorkers=2, latitude=0, currency=le, applied=false,
salary=1250} }
Any idea what am I doing wrong?
After several trials
.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Job job = snapshot.getValue(Job.class);
System.out.println(job.getTitle());
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.i(getClass().getName().toString(), ": " + databaseError.getMessage());
}
});
Using .addChildEventListener was the only Solution for my issue, Still investigation the reason.
i have edited your code and it should work fine now, i have used the same code snippets in several projects before. make sure your pojo matches the json data, you can use this tool to make it easier for yourself.
and the most important thing , Don't use Firebase as functions that return values - it goes against it's asynchronous nature.make Firebase perform it's task and then within the closure (block) go to the next step.
P.S: before using ValueEventListener or ChildEventListener read the difference between them from here.
public void getAllJobs() {
Posts posts= new Posts();
postsDBRef= FirebaseDatabase.getInstance();
postsDBRef.child("posts").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot jobSnapshot: dataSnapshot.getChildren()) {
Post post= jobSnapshot.getValue(Post.class);
posts.add(post);
}
// go to next step from here e.g handlePosts(posts);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.i(getClass().getName().toString(), ": " + databaseError.getMessage());
}
});
}
I want to get list of all Allowed child from this type of JSON Tree:
databaseRef.child('Users').child('Allowded').addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange (DataSnapshot dataSnapshot) {
//
}
}
#Override
public void onCancelled (DatabaseError databaseError) {
} };);
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference userRef = database.getReference("users").child(key).child("Alloweded");
ValueEventListener postListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User userObj = dataSnapshot.getValue(User.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
};userRef.addValueEventListener(postListener);
User is your Model class which have lat, lng, name,no., profileUrl etc
Try this I hope it works fine.
Firebase listeners fire for both the initial data and any changes.
If you're looking to synchronize the data in a collection, use ChildEventListener. If you're looking to synchronize a single object, use ValueEventListener. Note that in both cases you're not "getting" the data. You're synchronizing it, which means that the callback may be invoked multiple times: for the initial data and whenever the data gets updated.
FirebaseRef.child("message").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println(snapshot.getValue()); //prints "Do you have data? You'll
love Firebase."
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
databaseRef.child('Users').child('Allowded').addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange (DataSnapshot dataSnapshot) {
for (DataSnapshot childDataSnapshot : dataSnapshot.getChildren()) {
Log.d(TAG, "onDataChange: 1 " + childDataSnapshot.getKey());
for (DataSnapshot childDataSnapshot2 : childDataSnapshot.getChildren()){
Log.d(TAG, "onDataChange: 2 " + childDataSnapshot2.getKey());
}
}
}
}
#Override
public void onCancelled (DatabaseError databaseError) {
} };);