Search code examples
androidgoogle-mapsfirebase-realtime-databasegeofire

how to retrieve continuous updated location data from the GeoFire and put it on Google map?


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??


Solution

  • 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.

    Scenario 1: Show/store only the last known location.

    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);
    

    Scenario 2: Show the last 5 locations.

    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);
    


    As this data is sensitive in nature, ensure that you also have processes to completely delete all data concerning a parent and their children in a fast and simple manner.