Is it possible to have a FirebaseUI ListView order itself by a property of the local data model (In this case, Person.class) as opposed to a value contained in the database... in realtime no less?
The problem: I am converting a heavily location-based app (similar to a ridesharing app) from PHP/mysql to firebase. (I'm very excited about Firebase, by the way... and I have already seen many advantages).
For our purposes, the app simply displays a listview represting nearby and available service providers ordered by distance. This was previously done by sending a PHP request including the customer's latitude and longitude as POST parameters to the server. The server would then send back a JSON list of service providers. The server would do the math of ordering the results based on the service provider's current distance to the customer. Also... service providers that are not available are not returned at all. The problem with all this is that user's still needed to "pull to refresh."
The Firebase approach is working so far, but I have only been able to filter my (FirebaseUI powered) listview by service provider's availability (see code), but I haven't been able to think of how to order the list by distance.
The code below shows what I'm trying to do. The Person class represents service providers.
So... let's say the Person class has variable Person.distance and a getDistance() method that returns the calculated distance between their location (from Firebase database) and the location stored currently in the app user's phone. This variable obviously can't be stored in the database and used as a comparable value because it is relative and depends on the location of both the user and the service provider.
Is there a way to acheive this using a recycler view, instead? I would assume that it's going to be something done inside the adapter... but that is all wrapped up in the FirebaseUI already... and I'm not good enough at coding to re-write Android library classes. Thanks for any help.
Pertinent code (works fine... that's not the issue... please read post first):
ref = FirebaseDatabase.getInstance().getReference().child("person");
Query orderedByAvail_Query = ref.orderByChild("available").equalTo(true);
FirebaseListAdapter<Person> fireAdapter = new FirebaseListAdapter<Person>(getActivity(),
Person.class, R.layout.single_row, orderedByAvail_Query) {
@Override
protected void populateView(View v, Person person, int position) {
TextView name_title = (TextView) v.findViewById(R.id.Nickname);
name_title.setText(person.getNickname());
if (person.getAvail()){
name_title.setBackgroundColor(Color.GREEN);
} else {
name_title.setBackgroundColor(Color.WHITE);
}
}
};
// Arraylist<Person> tempList4Sorting = new ArrayList<Person>;
// for (i=1 to fireAdapter.size){
// tempList4Sorting.add(fireAdapter.getItem(i);
// i++;
// }
// Collections.sort(tempList4Sorting, Person.distance, DESCENDING);
// fireAdapter.clearItems();
//
// put sorted items back into the adapter using another for loop
// fireAdapter.notifyDataSetChanged (?)
setListAdapter(fireAdapter);
I achieved what appears to be a working solution... although I definitely opted for simplicity over efficiency. I don't think I really "re-wrote" anything -- more like playing the old game, "Operation." Anyway... Here's what I did:
1) Made the Person class implement Comparable so that Collection.sort() will work. In the compareTo() method, the sort is done via Person.getDistance() which simply calculates the distance between the lat/lon from the GPS of the phone that the app is running on, and the lat/lon of the person (retreived from the Firebase database).
2) Changed the getItem() method in FirebaseListActivity as follows:
@Override
public T getItem(int position) {
Log.i("XXX", "getItem() was called");
// put all (parsed) list items into an array
ArrayList<Person> temp_person_list = new ArrayList();
for (int i = 1; i<=(mSnapshots.getCount()); i++){
temp_person_list.add((Person) parseSnapshot(mSnapshots.getItem(i-1)));
}
// sort the array based on distance property
Collections.sort(temp_person_list);
// return the originally desired position
return (T) temp_person_list.get(position);
}
I know this is terrible in principle... (I'm sure a better way would be to add yet another underlying ArrayList inside the FirebaseArray class that silently re-maps indexes based on distance... but frankly that makes my head hurt).
Anyway... I've also considered another possible solution (like Geofire) -- using a geohash instead of lat/lon. That way there would only be ONE (sortable) location field instead of two... and it could possibly be queried to achieve a sorted (by proximity) list using existing Firebase query methods. Still working on this.