Reputation: 61
I am trying retrieve only the list of users who are within 3 miles of current users location. I am using a recyclerView to list the users, I am also using geofire to query the keys and I'm using firebase to store my users. When I try to retrieve only the list of users who are within 3 miles,it gives me all of the users including the ones who aren't within 3 miles. Can someone please help me fix this. Below is my code. Thanks in advance
MainClass To retrieve data
@Override
public void onSuccess(Location location) {
Double latitude=location.getLatitude();
Double longitude=location.getLongitude();
Geocoder geocoder=new Geocoder(NewsFeedActivity.this, Locale.getDefault());
firebaseDatabase=FirebaseDatabase.getInstance().getReference("Users");
GeoFire geoFire=new GeoFire(firebaseDatabase.child("locals"));
final GeoQuery geoQuery=geoFire.queryAtLocation(new GeoLocation(latitude,longitude),3);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
@Override
public void onKeyEntered(final String key, GeoLocation location) {
firebaseDatabase.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
list=new ArrayList<Users>();
for(DataSnapshot dataSnapshot1:dataSnapshot.getChildren()){
Users my=dataSnapshot1.getValue(My.class);
list.add(my);
}
adapter=new MyAdapter(MainActivity.this,list);
recycler.setAdapter(adapter);
adapter=new MyAdapter(MainActivity.this,list);
recycler.setAdapter(adapter);
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
@Override
public void onKeyExited(String key) {
}
@Override
public void onKeyMoved(String key, GeoLocation location) {
}
@Override
public void onGeoQueryReady() {
}
@Override
public void onGeoQueryError(DatabaseError error) {
System.err.println("There was an error with this query: " + error);
}
});
Adapter class for the recyclerView
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
Context context;
private ArrayList<UserInformation> users;
public MyAdapter(Context c,ArrayList<UserInformation> u){
context=c;
users=u;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.cardview,parent,false));
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.user_name.setText(users.get(position).getBusinessname());
holder.address.setText(users.get(position).getAddress());
}
@Override
public int getItemCount() {
return users.size();
}
class MyViewHolder extends RecyclerView.ViewHolder{
TextView user_name,address;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
user_name=(TextView)itemView.findViewById(R.id.user_name);
address=(TextView)itemView.findViewById(R.id.user_address);
}
}
}
//Users Class
public class Users {
public String usersname;
public String address;
public String phonenumber;
public Users(String usersname, String address, String phonenumber) {
this.usersname = usersname;
this.address = address;
this.phonenumber = phonenumber;
}
public Users(){
}
public String getBusinessname() {
return usersname;
}
public void setBusinessname(String businessname) {
this.usersname = usersname;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhonenumber() {
return phonenumber;
}
public void setPhonenumber(String phonenumber) {
this.phonenumber = phonenumber;
}
}
Upvotes: 1
Views: 671
Reputation: 61
I solved it with the Help of answers from Frank Van Puffelen. Here's what I did
public void onKeyEntered(final String key, GeoLocation location) {
firebaseDatabase.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
Users my=dataSnapshot.getValue(Users.class);
list.add(my);
adapter=new MyAdapter(MainActivity.this,list);
recycler.setAdapter(adapter);
}
Upvotes: 1
Reputation: 598728
Your geoquery correctly searches for keys within 3 kilometers from the given location:
firebaseDatabase=FirebaseDatabase.getInstance().getReference("Users");
GeoFire geoFire=new GeoFire(firebaseDatabase.child("locals"));
final GeoQuery geoQuery=geoFire.queryAtLocation(new GeoLocation(latitude,longitude),3);
But then when GeoFire finds a key within that range and calls onKeyEntered
, you add a ValueEventListener
to the entire Users
node:
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
@Override
public void onKeyEntered(final String key, GeoLocation location) {
firebaseDatabase.addValueEventListener(new ValueEventListener() {
So for each key within the geoquery's range, you load all users. If there are multiple keys within the range, you load all users for each key, and create an adapter each time. At best that is messy and wasteful, but I think in this case it's also why your code doesn't do what you want it to do.
My best guess is that each key in the geoquery identifies a node under Users
, and you only want to load that user's node each time your onKeyEntered
method is called. That way when there are multiple keys within the geoquery's range, you load each user within that range one by one.
If that is the case, you'll need this listener inside the onKeyEntered
:
firebaseDatabase.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
Users my=dataSnapshot.getValue(My.class);
list.add(my);
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore possible errors
}
});
Some of the changes I made here:
addListenerForSingleValueEvent
, instead of a continuous listener. This way you get the data for the user just once, instead of continuously adding more and more listeners.adapter.notifyDataSetChanged();
instead of creating a new adapter for each key that falls into the geoquery's range.What you'll still need to do:
onKeyEntered
, passing it the list
.Then when the onKeyEntered
fires, and you load the data of that user, you add it to the list
and tell the adapter that it needs to repaint with adapter.notifyDataSetChanged()
.
Upvotes: 3
Reputation: 180777
You can specify a center and radius several ways. Here is one way:
var geoQuery = geoFire.query({
center: [10.38, 2.41],
radius: 10.5
});
Note that radius is specified in kilometers.
Upvotes: 0