I'm working on a kids location tracking app..here i'm sending the kid's location on database using geoFire which updates after a certain amount of time . But problem is that, in the parents activity the location marker doesn't change depending on the updated location of the kid...So, How can I retrieve the continuous location updates form the database and move the marker according with the location??
It depends on what information you are trying to display.
In the following code examples, I have left the implementation of the ParentModel
, ChildModel
, LocationModel
and CheckInModel
classes up to you to develop. The code below also doesn't deal with initialising activities, Firebase or the GoogleMaps
object.
It goes without saying that you must take the utmost care to secure this sensitive data.
In this scenario, the database only ever holds a single location for each child. There is no concept of location history on the server.
This can be achieved by listening to updates to the data stored in the database at a single location (see Listen for Value Events). In the below code, I have also added an optional Polyline showing recent location updates stored on the client device while the map is open. Each time the data is updated on the server, the listener will be executed and update the local copy of the map.
private DatabaseReference mDatabase;
private DatabaseReference mTrackedLocationRef;
private ParentModel mParent;
private ChildModel mSelectedChild;
private GoogleMap mMap;
private Polyline mCurrentLine;
private Marker mLastKnownLocationMarker;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();
// initialize a tracking line
mCurrentLine = mMap.addPolyline((new PolylineOptions())
.clickable(true);
// TODO: download parent's user profile to mParent
mSelectedChild = mParent.children[0]; // select first child
mTrackedLocationRef = mDatabase.child("locations").child(mSelectedChild.uid).child("lastKnownLocation");
ValueEventListener locationUpdateListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// location was updated
LocationModel locModel = dataSnapshot.getValue(LocationModel.class);
LatLng pos = locModel.getLatLng(); // getLatLng() returns a LatLng object.
// if enabled, add position to traced line
if (mCurrentLine != null) {
mCurrentLine.add(pos);
}
// update/create marker for last detected location
if (mLastKnownLocationMarker != null) {
mLastKnownLocationMarker.setPosition(pos);
} else {
mLastKnownLocationMarker = mMap.addMarker(new MarkerOptions().position(pos)
.title(mSelectedChild.firstName + "'s current location"));
}
// move camera to new position
googleMap.moveCamera(CameraUpdateFactory.newLatLng(pos));
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Getting location update failed, log a message
Log.w(TAG, "locUpdate:onCancelled", databaseError.toException());
Toast.makeText(mContext, "Failed to load last known location.",
Toast.LENGTH_SHORT).show();
}
};
mTrackedLocationRef.addValueEventListener(locationUpdateListener);
In this scenario, a history of location "check-ins" is kept on the database that were uploaded to the server using push()
operations. The listener in this case will only update the map with the 5 most recent location updates. Each time a new location is added to the server, the new location will be added to the map and the oldest of the 5 locations will be removed.
private DatabaseReference mDatabase;
private DatabaseReference mTrackedLocationRef;
private ParentModel mParent;
private ChildModel mSelectedChild;
private GoogleMap mMap;
private Polyline mCurrentLine;
private final Map<String, Marker> mMarkers = new ConcurrentHashMap<String, Marker>();
// initialize a tracking line
mCurrentLine = mMap.addPolyline((new PolylineOptions())
.clickable(true);
// TODO: download parent's user profile to mParent
mSelectedChild = mParent.children[0]; // select first child
mTrackedLocationRef = mDatabase.child("locations").child(mSelectedChild.uid).child("checkins").limitToLast(5);
ChildEventListener locationUpdateListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
// this location update is one of the five most-recent entries
String keyName = dataSnapshot.getKey();
Log.d(TAG, "onChildAdded:" + keyName);
CheckInModel checkin = dataSnapshot.getValue(CheckInModel.class);
LatLng pos = checkin.getLatLng(); // getLatLng() returns a LatLng object.
// remove title of previous marker
mMarkers.get(previousChildName).setTitle("");
// add new marker
Marker marker = mMap.addMarker(new MarkerOptions().position(pos)
.title(mSelectedChild.firstName + "'s current location"));
marker.setTag(keyName); // store key name in marker's metadata
// store marker by its key name for easy removal
mMarkers.put(keyName, marker);
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
// todo: handle edited data?
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
// this location update is no longer one of the five most-recent entries
Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
// find and remove existing marker
mMarkers.get(dataSnapshot.getKey()).remove();
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
// todo: handle reordered data?
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "locUpdate:onCancelled", databaseError.toException());
Toast.makeText(mContext, "Failed to load location history.",
Toast.LENGTH_SHORT).show();
}
};
mTrackedLocationRef.addChildEventListener(locationUpdateListener);