Google Maps Android API V2 MyLocation LocationSource and event handling

The new Google Maps API V2 for Android doesn’t have much in the documentation or examples for how to deal with the myLocation layer.  I wanted to achieve similar functionality to the myLocationOverlay in the old API.  Basically, I want to wait until the user’s location is known, then center the map on that spot.

To do this with the new API, you need to define a custom LocationSource implementation, then respond appropriately when the user’s location is determined.  

Here is a simple class that asks for the user’s location then centers the map on their location once it becomes known:

public class MyLocationMapFragmentActivity extends FragmentActivity implements LocationListener, LocationSource
{
    /**
     * Note that this may be null if the Google Play services APK is not available.
     */
    private GoogleMap mMap;
    
    private OnLocationChangedListener mListener;
    private LocationManager locationManager;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.basic_map);
	    
        locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

    	    if(locationManager != null)
    	    {
    	        boolean gpsIsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    	        boolean networkIsEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    	    	
    	    	if(gpsIsEnabled)
    	    	{
    	    		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000L, 10F, this);
    	    	}
    	    	else if(networkIsEnabled)
    	    	{
    	    		locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000L, 10F, this);
    	    	}
    	    	else
    	    	{
    	    		//Show an error dialog that GPS is disabled...
                }
    	    }
    	    else
    	    {
    	    	//Show some generic error dialog because something must have gone wrong with location manager.
    	    }
        
        setUpMapIfNeeded();
    }

	@Override
	public void onPause()
	{
		if(locationManager != null)
		{
			locationManager.removeUpdates(this);
		}
		
		super.onPause();
	}
	
	@Override
	public void onResume()
	{
		super.onResume();
		
		setUpMapIfNeeded();
		
		if(locationManager != null)
		{
			mMap.setMyLocationEnabled(true);
		}
	}
	

    /**
     * Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly
     * installed) and the map has not already been instantiated.. This will ensure that we only ever
     * call {@link #setUpMap()} once when {@link #mMap} is not null.
     * <p>
     * If it isn't installed {@link SupportMapFragment} (and
     * {@link com.google.android.gms.maps.MapView
     * MapView}) will show a prompt for the user to install/update the Google Play services APK on
     * their device.
     * <p>
     * A user can return to this Activity after following the prompt and correctly
     * installing/updating/enabling the Google Play services. Since the Activity may not have been
     * completely destroyed during this process (it is likely that it would only be stopped or
     * paused), {@link #onCreate(Bundle)} may not be called again so we should call this method in
     * {@link #onResume()} to guarantee that it will be called.
     */
    private void setUpMapIfNeeded() {
        // Do a null check to confirm that we have not already instantiated the map.
        if (mMap == null) 
        {
            // Try to obtain the map from the SupportMapFragment.
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.basicMap)).getMap();
            // Check if we were successful in obtaining the map.
           
            if (mMap != null) 
            {
                setUpMap();
            }

            //This is how you register the LocationSource
            mMap.setLocationSource(this);
        }
    }
    
    /**
     * This is where we can add markers or lines, add listeners or move the camera.
     * <p>
     * This should only be called once and when we are sure that {@link #mMap} is not null.
     */
    private void setUpMap() 
    {
        mMap.setMyLocationEnabled(true);
    }
    
	@Override
	public void activate(OnLocationChangedListener listener) 
	{
		mListener = listener;
	}
	
	@Override
	public void deactivate() 
	{
		mListener = null;
	}

	@Override
	public void onLocationChanged(Location location) 
	{
	    if( mListener != null )
	    {
	        mListener.onLocationChanged( location );

	        mMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(location.getLatitude(), location.getLongitude())));
	    }
	}

	@Override
	public void onProviderDisabled(String provider) 
	{
		// TODO Auto-generated method stub
		Toast.makeText(this, "provider disabled", Toast.LENGTH_SHORT).show();
	}

	@Override
	public void onProviderEnabled(String provider) 
	{
		// TODO Auto-generated method stub
		Toast.makeText(this, "provider enabled", Toast.LENGTH_SHORT).show();
	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) 
	{
		// TODO Auto-generated method stub
		Toast.makeText(this, "status changed", Toast.LENGTH_SHORT).show();
	}
}

If you would like to prevent the user’s location from going “off-screen” by centering the map on their location if the location hits the edge of the map (like myLocationOverlay does in the old API), then replace onLocationChanged with something similar to this:

	@Override
	public void onLocationChanged(Location location) 
	{
	    if( mListener != null )
	    {
	        mListener.onLocationChanged( location );

	        LatLngBounds bounds = this.mMap.getProjection().getVisibleRegion().latLngBounds;

	        if(!bounds.contains(new LatLng(location.getLatitude(), location.getLongitude())))
	        {
	             //Move the camera to the user's location once it's available!
	             mMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(location.getLatitude(), location.getLongitude())));
	        }
	    }
	}

That’s it for now, enjoy!

8 thoughts on “Google Maps Android API V2 MyLocation LocationSource and event handling

      1. Micron

        Thanks. I did, but doesn’t work. Is there example package for downloading?
        I also tried locationManager.getLastKnownLocation, and it returned with a valid location. The location service is working I believe.

      2. discgolfsoftware Post author

        See my updated code. I’ve found that using the “getBestProvider” methods just plain don’t work on some devices. Lots of users were complaining about my app being unable to find their location, so I had to fall back to manually picking GPS or Network. I think if you update your code to use the location manager code I just added, it will work just fine.

  1. Name Surname

    Hi,
    it works for me, thanks! 🙂
    Wanted to add, you should call

    mMap.setLocationSource(this);

    after the null check, as .getMap() can return null.

    Reply
  2. Static

    Thanks for the great tutorial.

    My question is, Map Camera automatically animates to user location.
    How can we handle the map camera moves to the user location only if “My Location” button is pressed.

    Regards,
    Volkan

    Reply
    1. discgolfsoftware Post author

      Hi Volkan,

      The behavior you described is the default behavior of the My Location button – just get rid of the mMap.setLocationSource(this) call and it won’t automatically animate to their location.

      Reply

Leave a comment