How to Get My Location Changed Event with Google Maps Android API V2

How to get My Location changed event with Google Maps android API v2?

UPDATE: Google introduced the new LocationClient and associated LocationListener (the OnMyLocationChangeListener interface is now deprecated).


You can do this by creating a custom LocationSource for the my-location layer. Below is an example that automatically centers the camera on current location ("my location"), similar to the functionality that was offered by MyLocationOverlay in Google Maps Android API v1.

So you can simply replace this line of code

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

with whatever functionality you need to be executed when the location changes.

public class PlaceMapFragment extends SupportMapFragment {

// Note that this may be null if the Google Play services APK is not available.
private GoogleMap mMap;

protected PlaceActivity activity;
private FollowMeLocationSource followMeLocationSource;
private Context mContext;

/* We need the Context in order to get a reference to the Location Manager
* (when instantiating this fragment from your activity use:
* PlaceMapFragment mapFragment = new PlaceMapFragment(this); ) */
public PlaceMapFragment(Context context) {
this.mContext = context;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

activity = (PlaceActivity)getActivity();
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// creates our custom LocationSource and initializes some of its members
followMeLocationSource = new FollowMeLocationSource();

/* We can't be guaranteed that the map is available because Google Play services might not be available.
* (un-comment the following line when using this code in a FragmentActivity / Activity
* to try get a reference to the map here !) */
//setUpMapIfNeeded();
}

@Override
public void onResume() {
super.onResume();

/* We query for the best Location Provider everytime this fragment is displayed
* just in case a better provider might have become available since we last displayed it */
followMeLocationSource.getBestAvailableProvider();

// Get a reference to the map/GoogleMap object
setUpMapIfNeeded();

/* Enable the my-location layer (this causes our LocationSource to be automatically activated.)
* While enabled, the my-location layer continuously draws an indication of a user's
* current location and bearing, and displays UI controls that allow a user to interact
* with their location (for example, to enable or disable camera tracking of their location and bearing).*/
mMap.setMyLocationEnabled(true);
}

@Override
public void onPause() {
/* Disable the my-location layer (this causes our LocationSource to be automatically deactivated.) */
mMap.setMyLocationEnabled(false);

super.onPause();
}

/**
* 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
* manipulate the map once when it {@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.
*/
private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the map.
if (mMap == null) {
mMap = getMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
// The Map is verified. It is now safe to manipulate the map:

// Replace the (default) location source of the my-location layer with our custom LocationSource
mMap.setLocationSource(followMeLocationSource);

// Set default zoom
mMap.moveCamera(CameraUpdateFactory.zoomTo(15f));
}
}
}

/* Our custom LocationSource.
* We register this class to receive location updates from the Location Manager
* and for that reason we need to also implement the LocationListener interface. */
private class FollowMeLocationSource implements LocationSource, LocationListener {

private OnLocationChangedListener mListener;
private LocationManager locationManager;
private final Criteria criteria = new Criteria();
private String bestAvailableProvider;
/* Updates are restricted to one every 10 seconds, and only when
* movement of more than 10 meters has been detected.*/
private final int minTime = 10000; // minimum time interval between location updates, in milliseconds
private final int minDistance = 10; // minimum distance between location updates, in meters

private FollowMeLocationSource() {
// Get reference to Location Manager
locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);

// Specify Location Provider criteria
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setPowerRequirement(Criteria.POWER_LOW);
criteria.setAltitudeRequired(true);
criteria.setBearingRequired(true);
criteria.setSpeedRequired(true);
criteria.setCostAllowed(true);
}

private void getBestAvailableProvider() {
/* The preffered way of specifying the location provider (e.g. GPS, NETWORK) to use
* is to ask the Location Manager for the one that best satisfies our criteria.
* By passing the 'true' boolean we ask for the best available (enabled) provider. */
bestAvailableProvider = locationManager.getBestProvider(criteria, true);
}

/* Activates this provider. This provider will notify the supplied listener
* periodically, until you call deactivate().
* This method is automatically invoked by enabling my-location layer. */
@Override
public void activate(OnLocationChangedListener listener) {
// We need to keep a reference to my-location layer's listener so we can push forward
// location updates to it when we receive them from Location Manager.
mListener = listener;

// Request location updates from Location Manager
if (bestAvailableProvider != null) {
locationManager.requestLocationUpdates(bestAvailableProvider, minTime, minDistance, this);
} else {
// (Display a message/dialog) No Location Providers currently available.
}
}

/* Deactivates this provider.
* This method is automatically invoked by disabling my-location layer. */
@Override
public void deactivate() {
// Remove location updates from Location Manager
locationManager.removeUpdates(this);

mListener = null;
}

@Override
public void onLocationChanged(Location location) {
/* Push location updates to the registered listener..
* (this ensures that my-location layer will set the blue dot at the new/received location) */
if (mListener != null) {
mListener.onLocationChanged(location);
}

/* ..and Animate camera to center on that location !
* (the reason for we created this custom Location Source !) */
mMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(location.getLatitude(), location.getLongitude())));
}

@Override
public void onStatusChanged(String s, int i, Bundle bundle) {

}

@Override
public void onProviderEnabled(String s) {

}

@Override
public void onProviderDisabled(String s) {

}
}

}

How to get location and its updates in Google Maps Android API V2

For question 2 :

Use requestLocationUpdates(long minTime, float minDistance, Criteria criteria, PendingIntent intent)

First two parameters are :

Parameters
minTime : minimum time interval between location updates, in milliseconds
minDistance : minimum distance between location updates, in meters

           private static final long POLLING_FREQ = 1000 * 10;
private static final float MIN_DISTANCE = 10.0f;

// Reference to the LocationManager and LocationListener
private LocationManager mLocationManager;
private LocationListener mLocationListener;

// Register for network location updates
if (null != mLocationManager
.getProvider(LocationManager.NETWORK_PROVIDER)) {
mLocationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, POLLING_FREQ,
MIN_DISTANCE, mLocationListener);
}

// Register for GPS location updates
if (null != mLocationManager
.getProvider(LocationManager.GPS_PROVIDER)) {
mLocationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, POLLING_FREQ,
MIN_DISTANCE, mLocationListener);
}

You can adjust them to your own needs.

For question 3 :

Use : removeUpdates(PendingIntent intent) Removes all location updates for the specified pending intent.

As of question 1.

Your approach seems reasonable. If you need constant location updates if not then I can suggest other things to reduce battery consumption.

     mLocationListener = new LocationListener() {

// Called back when location changes

public void onLocationChanged(Location location) {

// Get your location here
// estimate

}

public void onStatusChanged(String provider, int status,
Bundle extras) {
// NA
}

public void onProviderEnabled(String provider) {
// NA
}

public void onProviderDisabled(String provider) {
// NA
}
};

Android Maps API v2 - event onClick on my location cursor

Maybe you should put a custom marker on your own position with the same imageResource as the default own-location marker with an own onClickListener, this way it will look the same as the built-in one, just don't forget to track the user's location, and update the marker at every location changed event.

Google maps api v2 Android Not updating on locationListener

If you just need a one time location I would use getLastKnowLocation from LocationManager that way you dont always have the GPS running for no reason.

EDIT:

well first of all I dont understand why you need to have that whole big class to get the location. the map itself does have a location listener

GoogleMap map = getMap();
map.setOnMyLocationChangeListener(this);

then in your map fragment implement OnMyLocationChangeListener and you will get callbacks when your location changes plus your initial location on first run like you want.

so that location class is basically overkill and useless unless you need to something special with the location manager.

Second, when you create the FollowMeLocationSource class I dont see anywhere where you request location updates which would be why it is not getting fired

Google Maps Android API v2 - restoring map state

I don't think you can, but you can save your CameraPosition which has your Position/Zoom/Angle...

http://developer.android.com/reference/com/google/android/gms/maps/model/CameraPosition.html

so you can write a function in your onDestroy which gets the CameraPosition from your map and store it in your SharedPreferences. In your onCreate() you recreate your CameraPosition from the SharedPreferences (after your map is instanciated).

// somewhere in your onDestroy()
@Override
protected void onDestroy() {
CameraPosition mMyCam = MyMap.getCameraPosition();
double longitude = mMyCam.target.longitude;
(...)

SharedPreferences settings = getSharedPreferences("SOME_NAME", 0);
SharedPreferences.Editor editor = settings.edit();
editor.putDouble("longitude", longitude);
(...) //put all other values like latitude, angle, zoom...
editor.commit();
}

in your onCreate()

SharedPreferences settings = getSharedPreferences("SOME_NAME", 0);
// "initial longitude" is only used on first startup
double longitude = settings.getDouble("longitude", "initial_longitude");
(...) //add the other values

LatLng startPosition = new LatLng() //with longitude and latitude

CameraPosition cameraPosition = new CameraPosition.Builder()
.target(startPosition) // Sets the center of the map to Mountain View
.zoom(17) // Sets the zoom
.bearing(90) // Sets the orientation of the camera to east
.tilt(30) // Sets the tilt of the camera to 30 degrees
.build(); // Creates a CameraPosition from the builder

create a new cameraPosition and animate it. be sure, map is instanziated at that point

 map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

Listen to My Location icon tap event in Google Maps API v2

Since this question was posted, OnMyLocationChangeListener was added to the API, making the implementation of a workaround (a lot) easier (i.e. there's no need for a custom LocationSource anymore).

So you can draw a Marker (with a similar icon) on top of My Location dot in order to receive the corresponding onMarkerClick() callback.

public class DemoMapFragment extends SupportMapFragment implements OnMyLocationChangeListener, OnMarkerClickListener { 

// Note that 'mMap' may be null if the Google Play services APK is not available.
private GoogleMap mMap;
private Marker myLocationMarker;
private static BitmapDescriptor markerIconBitmapDescriptor;
/* ... */

@Override
public void onResume() {
super.onResume();
setUpMapIfNeeded(); // Get a reference to the map
mMap.setMyLocationEnabled(true); // Enable the my-location layer
mMap.setOnMyLocationChangeListener(this);
mMap.setOnMarkerClickListener(this);
}

private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the map.
if (mMap == null) {
mMap = getMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
// The Map is verified. It is now safe to manipulate the map:

// Load custom marker icon
markerIconBitmapDescriptor = BitmapDescriptorFactory.fromResource(R.drawable.my_location_dot_icon);

// When the map is first loaded we need to add our marker on top of My Location dot
myLocationMarker = mMap.addMarker(new MarkerOptions()
.position(new LatLng(mMap.getMyLocation().getLatitude(),mMap.getMyLocation().getLongitude()))
.icon(markerIconBitmapDescriptor));

// Set default zoom
mMap.moveCamera(CameraUpdateFactory.zoomTo(15f));
}
}
}

@Override
public void onMyLocationChange(Location location) {
// Remove the old marker object
myLocationMarker.remove();

// Add a new marker object at the new (My Location dot) location
myLocationMarker = mMap.addMarker(new MarkerOptions()
.position(new LatLng(location().getLatitude(),location().getLongitude()))
.icon(markerIconBitmapDescriptor));
}

@Override
public boolean onMarkerClick(Marker marker) {
if (marker.equals(myLocationMarker)) {
/* My Location dot callback ... */
}
}
}

Determine camera change event was programatic

You can set a boolean before you change the camera programatically, and check if it is set (and unset) in the onCameraChange method.

Something like this:

// Moving programmatically
cameraMovedProgrammatically = true;
map.animateCamera(cameraUpdate);

And checking it:

public void onCameraChange(CameraPosition position) {
if (cameraMovedProgrammatically) {
// this is not a user event
cameraMovedProgrammatically = false;
} else {
// this is a user event
}
}

Google Maps Android API v2 - detect touch on map

@ape wrote an answer here on how to intercept the map clicks, but I need to intercept the touches, and then he suggested the following link in a comment of its answer, How to handle onTouch event for map in Google Map API v2?.

That solution seems to be a possible workaround, but the suggested code was incomplete. For this reason I rewrote and tested it, and now it works.

Here it is the working code:

I created the class MySupportMapFragment.java

import com.google.android.gms.maps.SupportMapFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MySupportMapFragment extends SupportMapFragment {
public View mOriginalContentView;
public TouchableWrapper mTouchView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState);
mTouchView = new TouchableWrapper(getActivity());
mTouchView.addView(mOriginalContentView);
return mTouchView;
}

@Override
public View getView() {
return mOriginalContentView;
}
}

I even created the class TouchableWrapper.java:

import android.content.Context;
import android.view.MotionEvent;
import android.widget.FrameLayout;

public class TouchableWrapper extends FrameLayout {

public TouchableWrapper(Context context) {
super(context);
}

@Override
public boolean dispatchTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:
MainActivity.mMapIsTouched = true;
break;

case MotionEvent.ACTION_UP:
MainActivity.mMapIsTouched = false;
break;
}
return super.dispatchTouchEvent(event);
}
}

In the layout I declare it this way:

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="@+id/buttonBar"
class="com.myFactory.myApp.MySupportMapFragment"
/>

Just for test in the main Activity I wrote only the following:

public class MainActivity extends FragmentActivity {
public static boolean mMapIsTouched = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

Google Maps v2 my location with Google Play

If i understand correctly you have defined the location update method, but have not started requesting the location updates.

To send the request for location updates, create a location client and a request in onCreate():

protected void onCreate(Bundle savedInstanceState) {
...
mLocationClient = new LocationClient(this, this, this);
mLocationRequest = LocationRequest.create();
}

Then connect it in onStart():

protected void onStart() {
...
mLocationClient.connect();
}

Then make the update request in onConnected():

public void onConnected(Bundle dataBundle) {
...
mLocationClient.requestLocationUpdates(mLocationRequest, this);
}

Here is a complete guide on how to do this correctly:
http://developer.android.com/training/location/receive-location-updates.html#StartUpdates

The Google Play services resources were not found. error is a common bug in the library.



Related Topics



Leave a reply



Submit