diff options
author | Eric Gilmore <egilmore@google.com> | 2015-03-10 15:47:43 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-03-10 15:47:43 +0000 |
commit | 2977c05fec4e91f9145074a9792c0dcb45b0782d (patch) | |
tree | 3265821c92f924e8871428a3c64e51b7a1f38bc4 /docs | |
parent | c9f94b9c81a3db0c41a6cabff47053e7cb5e32bd (diff) | |
parent | 89d0c0fbaec976b8b06c81a2950aed3a3c5e5b1b (diff) | |
download | frameworks_base-2977c05fec4e91f9145074a9792c0dcb45b0782d.zip frameworks_base-2977c05fec4e91f9145074a9792c0dcb45b0782d.tar.gz frameworks_base-2977c05fec4e91f9145074a9792c0dcb45b0782d.tar.bz2 |
am 89d0c0fb: am bcea1fcd: am f1eab6f5: am 39d47c7c: am a2d342e3: am beed9332: am 0bceaacf: Merge "Reworking geofencing training to new API and sample." into lmp-docs
* commit '89d0c0fbaec976b8b06c81a2950aed3a3c5e5b1b':
Reworking geofencing training to new API and sample.
Diffstat (limited to 'docs')
-rw-r--r-- | docs/html/images/training/geofence.png | bin | 0 -> 141763 bytes | |||
-rw-r--r-- | docs/html/images/training/geofence@2x.png | bin | 0 -> 275832 bytes | |||
-rw-r--r-- | docs/html/training/location/geofencing.jd | 1499 | ||||
-rw-r--r-- | docs/html/training/location/index.jd | 6 | ||||
-rw-r--r-- | docs/html/training/training_toc.cs | 4 |
5 files changed, 209 insertions, 1300 deletions
diff --git a/docs/html/images/training/geofence.png b/docs/html/images/training/geofence.png Binary files differnew file mode 100644 index 0000000..2d5d3aa --- /dev/null +++ b/docs/html/images/training/geofence.png diff --git a/docs/html/images/training/geofence@2x.png b/docs/html/images/training/geofence@2x.png Binary files differnew file mode 100644 index 0000000..2f83105 --- /dev/null +++ b/docs/html/images/training/geofence@2x.png diff --git a/docs/html/training/location/geofencing.jd b/docs/html/training/location/geofencing.jd index 748b6ec..59fc4c6 100644 --- a/docs/html/training/location/geofencing.jd +++ b/docs/html/training/location/geofencing.jd @@ -9,9 +9,11 @@ trainingnavtop=true <h2>This lesson teaches you to</h2> <ol> - <li><a href="#RequestGeofences">Request Geofence Monitoring</a></li> + <li><a href="#RequestGeofences">Set up for Geofence Monitoring</a></li> + <li><a href="#CreateAdd">Create and Add Geofences</a></li> <li><a href="#HandleGeofenceTransitions">Handle Geofence Transitions</a></li> <li><a href="#StopGeofenceMonitoring">Stop Geofence Monitoring</a></li> + </ol> <h2>You should also read</h2> @@ -23,577 +25,148 @@ trainingnavtop=true <h2>Try it out</h2> -<div class="download-box"> - <a href="http://developer.android.com/shareables/training/GeofenceDetection.zip" class="button">Download the sample</a> - <p class="filename">GeofenceDetection.zip</p> -</div> + <ul> + <li> + <a href="https://github.com/googlesamples/android-play-location/tree/master/Geofencing" + class="external-link">Geofencing</a> + </li> + </ul> </div> </div> <p> - Geofencing combines awareness of the user's current location with awareness of nearby - features, defined as the user's proximity to locations that may be of interest. To mark a + Geofencing combines awareness of the user's current location with awareness of the user's + proximity to locations that may be of interest. To mark a location of interest, you specify its latitude and longitude. To adjust the proximity for the - location, you add a radius. The latitude, longitude, and radius define a geofence. - You can have multiple active geofences at one time. + location, you add a radius. The latitude, longitude, and radius define a geofence, creating a + circular area, or fence, around the location of interest. </p> <p> - Location Services treats a geofences as an area rather than as a points and proximity. This - allows it to detect when the user enters or exits a geofence. For each geofence, you can ask - Location Services to send you entrance events or exit events or both. You can also limit the - duration of a geofence by specifying an expiration duration in milliseconds. After the geofence - expires, Location Services automatically removes it. + You can have multiple active geofences, with a limit of 100 per device user. For each geofence, + you can ask Location Services to send you entrance and exit events, or you can specify a + duration within the geofence area to wait, or <em>dwell</em>, before triggering an event. You + can limit the duration of any geofence by specifying an expiration duration in milliseconds. + After the geofence expires, Location Services automatically removes it. </p> -<!-- - Send geofences to Location Services - --> -<h2 id="RequestGeofences">Request Geofence Monitoring</h2> + +<img src="{@docRoot}images/training/geofence@2x.png" +srcset="{@docRoot}images/training/geofence.png 1x, {@docRoot}images/training/geofence@2x.png 2x" alt="" + width="400" height="400"/> +<p> + This lesson shows you how to add and remove geofences, and then listen for geofence transitions + using an {@link android.app.IntentService}.</p> + +<h2 id="RequestGeofences">Set up for Geofence Monitoring</h2> <p> The first step in requesting geofence monitoring is to request the necessary permission. To use geofencing, your app must request {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. To request this permission, add the following element as a child element of the <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code> - element: + element in your app manifest: </p> <pre> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> </pre> -<!-- Check for Google Play services --> -<h3>Check for Google Play Services</h3> -<p> - Location Services is part of the Google Play services APK. Since it's hard to anticipate the - state of the user's device, you should always check that the APK is installed before you attempt - to connect to Location Services. To check that the APK is installed, call -<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>, - which returns one of the - integer result codes listed in the API reference documentation. If you encounter an error, - call -<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code> - to retrieve localized dialog that prompts users to take the correct action, then display - the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the - user to correct the problem, in which case Google Play services may send a result back to your - activity. To handle this result, override the method - {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} -</p> -<p class="note"> - <strong>Note:</strong> To make your app compatible with - platform version 1.6 and later, the activity that displays the - {@link android.support.v4.app.DialogFragment} must subclass - {@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using - {@link android.support.v4.app.FragmentActivity} also allows you to call - {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager - getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}. -</p> <p> - Since you usually need to check for Google Play services in more than one place in your code, - define a method that encapsulates the check, then call the method before each connection - attempt. The following snippet contains all of the code required to check for Google - Play services: + If you want to use an {@link android.app.IntentService} to listen for geofence transitions, + add an element specifying the service name. This element must be + a child of the <code><a href="{@docRoot}guide/topics/manifest/application-element.html"> + <application></a></code> element: </p> -<pre> -public class MainActivity extends FragmentActivity { - ... - // Global constants - /* - * Define a request code to send to Google Play services - * This code is returned in Activity.onActivityResult - */ - private final static int - CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000; - ... - // Define a DialogFragment that displays the error dialog - public static class ErrorDialogFragment extends DialogFragment { - // Global field to contain the error dialog - private Dialog mDialog; - ... - // Default constructor. Sets the dialog field to null - public ErrorDialogFragment() { - super(); - mDialog = null; - } - ... - // Set the dialog to display - public void setDialog(Dialog dialog) { - mDialog = dialog; - } - ... - // Return a Dialog to the DialogFragment. - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - return mDialog; - } - ... - } - ... - /* - * Handle results returned to the FragmentActivity - * by Google Play services - */ - @Override - protected void onActivityResult( - int requestCode, int resultCode, Intent data) { - // Decide what to do based on the original request code - switch (requestCode) { - ... - case CONNECTION_FAILURE_RESOLUTION_REQUEST : - /* - * If the result code is Activity.RESULT_OK, try - * to connect again - */ - switch (resultCode) { - ... - case Activity.RESULT_OK : - /* - * Try the request again - */ - ... - break; - } - ... - } - ... - } - ... - private boolean servicesConnected() { - // Check that Google Play services is available - int resultCode = - GooglePlayServicesUtil. - isGooglePlayServicesAvailable(this); - // If Google Play services is available - if (ConnectionResult.SUCCESS == resultCode) { - // In debug mode, log the status - Log.d("Geofence Detection", - "Google Play services is available."); - // Continue - return true; - // Google Play services was not available for some reason - } else { - // Get the error code - int errorCode = connectionResult.getErrorCode(); - // Get the error dialog from Google Play services - Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog( - errorCode, - this, - CONNECTION_FAILURE_RESOLUTION_REQUEST); - // If Google Play services can provide an error dialog - if (errorDialog != null) { - // Create a new DialogFragment for the error dialog - ErrorDialogFragment errorFragment = - new ErrorDialogFragment(); - // Set the dialog in the DialogFragment - errorFragment.setDialog(errorDialog); - // Show the error dialog in the DialogFragment - errorFragment.show( - getSupportFragmentManager(), - "Geofence Detection"); - } - } - } - ... -} +<pre> +<application + android:allowBackup="true"> + ... + <service android:name=".GeofenceTransitionsIntentService"/> +<application/> </pre> -<p> - Snippets in the following sections call this method to verify that Google Play services is - available. -</p> -<p> - To use geofencing, start by defining the geofences you want to monitor. Although you usually - store geofence data in a local database or download it from the network, you need to send - a geofence to Location Services as an instance of -<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code>, - which you create with -<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.html">Geofence.Builder</a></code>. - Each -<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code> - object contains the following information: -</p> -<dl> - <dt>Latitude, longitude, and radius</dt> - <dd> - Define a circular area for the geofence. Use the latitude and longitude to mark a location - of interest, and then use the radius to adjust how close the user needs to approach the - location before the geofence is detected. The larger the radius, the more likely the - user will trigger a geofence transition alert by approaching the geofence. For example, - providing a large radius for a geofencing app that turns on lights in the user's house as - the user returns home might cause the lights to go on even if the user is simply passing by. - </dd> - <dt>Expiration time</dt> - <dd> - How long the geofence should remain active. Once the expiration time is reached, Location - Services deletes the geofence. Most of the time, you should specify an expiration time, but - you may want to keep permanent geofences for the user's home or place of work. - </dd> - <dt>Transition type</dt> - <dd> - Location Services can detect when the user steps within the radius of the geofence ("entry") - and when the user steps outside the radius of the geofence ("exit"), or both. - </dd> - <dt>Geofence ID</dt> - <dd> - A string that is stored with the geofence. You should make this unique, so that you can - use it to remove a geofence from Location Services tracking. - </dd> -</dl> -<h3>Define geofence storage</h3> -<p> - A geofencing app needs to read and write geofence data to persistent storage. You shouldn't use -<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code> - objects to do this; instead, use storage techniques such as databases that can store groups of - related data. + +<p>To access the location APIs, you need to create an instance of the + Google Play services API client. To learn how to connect your client, see + <a href="{@docRoot}training/location/retrieve-current.html#play-services">Connect + to Google Play Services</a>.</p> + +<h2 id="CreateAdd">Create and Add Geofences</h2> + +<p>Your app needs to create and add geofences using the location API's builder class for + creating Geofence objects, and the convenience class for adding them. Also, to handle the + intents sent from Location Services when geofence transitions occur, you can define a + {@link android.app.PendingIntent} as shown in this section. </p> + +<h3>Create geofence objects</h3> + <p> - As an example of storing geofence data, the following snippet defines two classes that use - the app's {@link android.content.SharedPreferences} instance for persistent storage. The class - {@code SimpleGeofence}, analogous to a database record, stores the - data for a single -<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code> - object in a "flattened" form. The class {@code SimpleGeofenceStore}, analogous to a database, - reads and writes {@code SimpleGeofence} data to the - {@link android.content.SharedPreferences} instance. -</p> -<pre> -public class MainActivity extends FragmentActivity { - ... - /** - * A single Geofence object, defined by its center and radius. - */ - public class SimpleGeofence { - // Instance variables - private final String mId; - private final double mLatitude; - private final double mLongitude; - private final float mRadius; - private long mExpirationDuration; - private int mTransitionType; + First, use <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder. + html">Geofence.Builder</a></code> to create a geofence, setting the desired radius, duration, and + transition types for the geofence. For example, to populate a list object named + {@code mGeofenceList}: + </p> - /** - * @param geofenceId The Geofence's request ID - * @param latitude Latitude of the Geofence's center. - * @param longitude Longitude of the Geofence's center. - * @param radius Radius of the geofence circle. - * @param expiration Geofence expiration duration - * @param transition Type of Geofence transition. - */ - public SimpleGeofence( - String geofenceId, - double latitude, - double longitude, - float radius, - long expiration, - int transition) { - // Set the instance fields from the constructor - this.mId = geofenceId; - this.mLatitude = latitude; - this.mLongitude = longitude; - this.mRadius = radius; - this.mExpirationDuration = expiration; - this.mTransitionType = transition; - } - // Instance field getters - public String getId() { - return mId; - } - public double getLatitude() { - return mLatitude; - } - public double getLongitude() { - return mLongitude; - } - public float getRadius() { - return mRadius; - } - public long getExpirationDuration() { - return mExpirationDuration; - } - public int getTransitionType() { - return mTransitionType; - } - /** - * Creates a Location Services Geofence object from a - * SimpleGeofence. - * - * @return A Geofence object - */ - public Geofence toGeofence() { - // Build a new Geofence object - return new Geofence.Builder() - .setRequestId(getId()) - .setTransitionTypes(mTransitionType) - .setCircularRegion( - getLatitude(), getLongitude(), getRadius()) - .setExpirationDuration(mExpirationDuration) - .build(); - } - } - ... - /** - * Storage for geofence values, implemented in SharedPreferences. - */ - public class SimpleGeofenceStore { - // Keys for flattened geofences stored in SharedPreferences - public static final String KEY_LATITUDE = - "com.example.android.geofence.KEY_LATITUDE"; - public static final String KEY_LONGITUDE = - "com.example.android.geofence.KEY_LONGITUDE"; - public static final String KEY_RADIUS = - "com.example.android.geofence.KEY_RADIUS"; - public static final String KEY_EXPIRATION_DURATION = - "com.example.android.geofence.KEY_EXPIRATION_DURATION"; - public static final String KEY_TRANSITION_TYPE = - "com.example.android.geofence.KEY_TRANSITION_TYPE"; - // The prefix for flattened geofence keys - public static final String KEY_PREFIX = - "com.example.android.geofence.KEY"; - /* - * Invalid values, used to test geofence storage when - * retrieving geofences - */ - public static final long INVALID_LONG_VALUE = -999l; - public static final float INVALID_FLOAT_VALUE = -999.0f; - public static final int INVALID_INT_VALUE = -999; - // The SharedPreferences object in which geofences are stored - private final SharedPreferences mPrefs; - // The name of the SharedPreferences - private static final String SHARED_PREFERENCES = - "SharedPreferences"; - // Create the SharedPreferences storage with private access only - public SimpleGeofenceStore(Context context) { - mPrefs = - context.getSharedPreferences( - SHARED_PREFERENCES, - Context.MODE_PRIVATE); - } - /** - * Returns a stored geofence by its id, or returns {@code null} - * if it's not found. - * - * @param id The ID of a stored geofence - * @return A geofence defined by its center and radius. See - */ - public SimpleGeofence getGeofence(String id) { - /* - * Get the latitude for the geofence identified by id, or - * INVALID_FLOAT_VALUE if it doesn't exist - */ - double lat = mPrefs.getFloat( - getGeofenceFieldKey(id, KEY_LATITUDE), - INVALID_FLOAT_VALUE); - /* - * Get the longitude for the geofence identified by id, or - * INVALID_FLOAT_VALUE if it doesn't exist - */ - double lng = mPrefs.getFloat( - getGeofenceFieldKey(id, KEY_LONGITUDE), - INVALID_FLOAT_VALUE); - /* - * Get the radius for the geofence identified by id, or - * INVALID_FLOAT_VALUE if it doesn't exist - */ - float radius = mPrefs.getFloat( - getGeofenceFieldKey(id, KEY_RADIUS), - INVALID_FLOAT_VALUE); - /* - * Get the expiration duration for the geofence identified - * by id, or INVALID_LONG_VALUE if it doesn't exist - */ - long expirationDuration = mPrefs.getLong( - getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION), - INVALID_LONG_VALUE); - /* - * Get the transition type for the geofence identified by - * id, or INVALID_INT_VALUE if it doesn't exist - */ - int transitionType = mPrefs.getInt( - getGeofenceFieldKey(id, KEY_TRANSITION_TYPE), - INVALID_INT_VALUE); - // If none of the values is incorrect, return the object - if ( - lat != GeofenceUtils.INVALID_FLOAT_VALUE && - lng != GeofenceUtils.INVALID_FLOAT_VALUE && - radius != GeofenceUtils.INVALID_FLOAT_VALUE && - expirationDuration != - GeofenceUtils.INVALID_LONG_VALUE && - transitionType != GeofenceUtils.INVALID_INT_VALUE) { +<pre> +mGeofenceList.add(new Geofence.Builder() + // Set the request ID of the geofence. This is a string to identify this + // geofence. + .setRequestId(entry.getKey()) - // Return a true Geofence object - return new SimpleGeofence( - id, lat, lng, radius, expirationDuration, - transitionType); - // Otherwise, return null. - } else { - return null; - } - } - /** - * Save a geofence. - * @param geofence The SimpleGeofence containing the - * values you want to save in SharedPreferences - */ - public void setGeofence(String id, SimpleGeofence geofence) { - /* - * Get a SharedPreferences editor instance. Among other - * things, SharedPreferences ensures that updates are atomic - * and non-concurrent - */ - Editor editor = mPrefs.edit(); - // Write the Geofence values to SharedPreferences - editor.putFloat( - getGeofenceFieldKey(id, KEY_LATITUDE), - (float) geofence.getLatitude()); - editor.putFloat( - getGeofenceFieldKey(id, KEY_LONGITUDE), - (float) geofence.getLongitude()); - editor.putFloat( - getGeofenceFieldKey(id, KEY_RADIUS), - geofence.getRadius()); - editor.putLong( - getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION), - geofence.getExpirationDuration()); - editor.putInt( - getGeofenceFieldKey(id, KEY_TRANSITION_TYPE), - geofence.getTransitionType()); - // Commit the changes - editor.commit(); - } - public void clearGeofence(String id) { - /* - * Remove a flattened geofence object from storage by - * removing all of its keys - */ - Editor editor = mPrefs.edit(); - editor.remove(getGeofenceFieldKey(id, KEY_LATITUDE)); - editor.remove(getGeofenceFieldKey(id, KEY_LONGITUDE)); - editor.remove(getGeofenceFieldKey(id, KEY_RADIUS)); - editor.remove(getGeofenceFieldKey(id, - KEY_EXPIRATION_DURATION)); - editor.remove(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE)); - editor.commit(); - } - /** - * Given a Geofence object's ID and the name of a field - * (for example, KEY_LATITUDE), return the key name of the - * object's values in SharedPreferences. - * - * @param id The ID of a Geofence object - * @param fieldName The field represented by the key - * @return The full key name of a value in SharedPreferences - */ - private String getGeofenceFieldKey(String id, - String fieldName) { - return KEY_PREFIX + "_" + id + "_" + fieldName; - } - } - ... -} + .setCircularRegion( + entry.getValue().latitude, + entry.getValue().longitude, + Constants.GEOFENCE_RADIUS_IN_METERS + ) + .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS) + .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | + Geofence.GEOFENCE_TRANSITION_EXIT) + .build()); </pre> -<h3>Create Geofence objects</h3> + +<p>This example pulls data from a constants file. In actual practice, apps might + dynamically create geofences based on the user's location.</p> + +<h3>Specify geofences and initial triggers</h3> + <p> - The following snippet uses the {@code SimpleGeofence} and {@code SimpleGeofenceStore} classes - gets geofence data from the UI, stores it in {@code SimpleGeofence} objects, stores these - objects in a {@code SimpleGeofenceStore} object, and then creates -<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code> - objects: + The following snippet uses the <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html"> + GeofencingRequest</a></code> class + and its nested <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.Builder.html"> + GeofencingRequestBuilder</a></code> class to + specify the geofences to monitor and to set how related geofence events are triggered: </p> <pre> -public class MainActivity extends FragmentActivity { - ... - /* - * Use to set an expiration time for a geofence. After this amount - * of time Location Services will stop tracking the geofence. - */ - private static final long SECONDS_PER_HOUR = 60; - private static final long MILLISECONDS_PER_SECOND = 1000; - private static final long GEOFENCE_EXPIRATION_IN_HOURS = 12; - private static final long GEOFENCE_EXPIRATION_TIME = - GEOFENCE_EXPIRATION_IN_HOURS * - SECONDS_PER_HOUR * - MILLISECONDS_PER_SECOND; - ... - /* - * Handles to UI views containing geofence data - */ - // Handle to geofence 1 latitude in the UI - private EditText mLatitude1; - // Handle to geofence 1 longitude in the UI - private EditText mLongitude1; - // Handle to geofence 1 radius in the UI - private EditText mRadius1; - // Handle to geofence 2 latitude in the UI - private EditText mLatitude2; - // Handle to geofence 2 longitude in the UI - private EditText mLongitude2; - // Handle to geofence 2 radius in the UI - private EditText mRadius2; - /* - * Internal geofence objects for geofence 1 and 2 - */ - private SimpleGeofence mUIGeofence1; - private SimpleGeofence mUIGeofence2; - ... - // Internal List of Geofence objects - List<Geofence> mGeofenceList; - // Persistent storage for geofences - private SimpleGeofenceStore mGeofenceStorage; - ... - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - ... - // Instantiate a new geofence storage area - mGeofenceStorage = new SimpleGeofenceStore(this); - - // Instantiate the current List of geofences - mCurrentGeofences = new ArrayList<Geofence>(); - } - ... - /** - * Get the geofence parameters for each geofence from the UI - * and add them to a List. - */ - public void createGeofences() { - /* - * Create an internal object to store the data. Set its - * ID to "1". This is a "flattened" object that contains - * a set of strings - */ - mUIGeofence1 = new SimpleGeofence( - "1", - Double.valueOf(mLatitude1.getText().toString()), - Double.valueOf(mLongitude1.getText().toString()), - Float.valueOf(mRadius1.getText().toString()), - GEOFENCE_EXPIRATION_TIME, - // This geofence records only entry transitions - Geofence.GEOFENCE_TRANSITION_ENTER); - // Store this flat version - mGeofenceStorage.setGeofence("1", mUIGeofence1); - // Create another internal object. Set its ID to "2" - mUIGeofence2 = new SimpleGeofence( - "2", - Double.valueOf(mLatitude2.getText().toString()), - Double.valueOf(mLongitude2.getText().toString()), - Float.valueOf(mRadius2.getText().toString()), - GEOFENCE_EXPIRATION_TIME, - // This geofence records both entry and exit transitions - Geofence.GEOFENCE_TRANSITION_ENTER | - Geofence.GEOFENCE_TRANSITION_EXIT); - // Store this flat version - mGeofenceStorage.setGeofence(2, mUIGeofence2); - mGeofenceList.add(mUIGeofence1.toGeofence()); - mGeofenceList.add(mUIGeofence2.toGeofence()); - } - ... +private GeofencingRequest getGeofencingRequest() { + GeofencingRequest.Builder builder = new GeofencingRequest.Builder(); + builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER); + builder.addGeofences(mGeofenceList); + return builder.build(); } </pre> + <p> - In addition to the {@link java.util.List} of -<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code> - objects you want to monitor, you need to provide Location Services with the - {@link android.content.Intent} that it sends to your app when it detects geofence - transitions. -<h4>Define a Intent for geofence transitions</h4> + This example shows the use of two geofence triggers. The <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER"> + GEOFENCE_TRANSITION_ENTER</a></code> + transition triggers when a device enters a geofence, and the <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_EXIT"> + GEOFENCE_TRANSITION_EXIT</a></code> + transition triggers when a device exits a geofence. Specifying + <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html#INITIAL_TRIGGER_ENTER"> + INITIAL_TRIGGER_ENTER</a></code> tells Location services that + <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER"> + GEOFENCE_TRANSITION_ENTER</a></code> + should be triggered if the the device is already inside the geofence.</p> +</p> + +<p>In many cases, it may be preferable to use instead <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html#INITIAL_TRIGGER_DWELL"> + INITIAL_TRIGGER_DWELL</a></code>, + which triggers events only when the user stops for a defined duration within a geofence. + This approach can help reduce "alert spam" resulting from large numbers notifications when a + device briefly enters and exits geofences. Another strategy for getting best results from your + geofences is to set a minimum radius of 100 meters. This helps account for the location accuracy + of typical WiFi networks, and also helps reduce device power consumption. +</p> + +<h3>Define an Intent for geofence transitions</h3> <p> The {@link android.content.Intent} sent from Location Services can trigger various actions in your app, but you should <i>not</i> have it start an activity or fragment, because components @@ -601,807 +174,133 @@ public class MainActivity extends FragmentActivity { {@link android.app.IntentService} is a good way to handle the intent. An {@link android.app.IntentService} can post a notification, do long-running background work, send intents to other services, or send a broadcast intent. The following snippet shows how - how to define a {@link android.app.PendingIntent} that starts an - {@link android.app.IntentService}: + to define a {@link android.app.PendingIntent} that starts an {@link android.app.IntentService}: </p> <pre> public class MainActivity extends FragmentActivity { ... - /* - * Create a PendingIntent that triggers an IntentService in your - * app when a geofence transition occurs. - */ - private PendingIntent getTransitionPendingIntent() { - // Create an explicit Intent - Intent intent = new Intent(this, - ReceiveTransitionsIntentService.class); - /* - * Return the PendingIntent - */ - return PendingIntent.getService( - this, - 0, - intent, - PendingIntent.FLAG_UPDATE_CURRENT); - } - ... -} -</pre> -<p> - Now you have all the code you need to send a request to monitor geofences to Location - Services. -</p> -<!-- Send the monitoring request --> -<h3 id="requestmonitoring">Send the monitoring request</h3> -<p> - Sending the monitoring request requires two asynchronous operations. The first operation gets a - location client for the request, and the second makes the request using the client. In both - cases, Location Services invokes a callback method when it finishes the operation. The best way - to handle these operations is to chain together the method calls. The following snippets - demonstrate how to set up an activity, define the methods, and call them in the proper order. -</p> -<p> - First, modify the activity's class definition to implement the necessary callback interfaces. - Add the following interfaces: -</p> -<dl> - <dt> -<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">ConnectionCallbacks</a></code> - </dt> - <dd> - Specifies methods that Location Services calls when a location client is connected or - disconnected. - </dd> - <dt> -<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html">OnConnectionFailedListener</a></code> - </dt> - <dd> - Specifies a method that Location Services calls if an error occurs while attempting to - connect the location client. - </dd> - <dt> -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html">OnAddGeofencesResultListener</a></code> - </dt> - <dd> - Specifies a method that Location Services calls once it has added the geofences. - </dd> -</dl> -<p> - For example: -</p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... -} -</pre> -<h4>Start the request process</h4> -<p> - Next, define a method that starts the request process by connecting to Location Services. - Mark this as a request to add a geofence by setting a global variable. This allows you to - use the callback -<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">ConnectionCallbacks.onConnected()</a></code> - to add geofences and to remove them, as described in succeeding sections. -</p> -<p> -<p> - To guard against race conditions that might arise if your app tries to start another request - before the first one finishes, define a boolean flag that tracks the state of the current - request: -</p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - // Holds the location client - private LocationClient mLocationClient; - // Stores the PendingIntent used to request geofence monitoring - private PendingIntent mGeofenceRequestIntent; - // Defines the allowable request types. - public enum REQUEST_TYPE = {ADD} - private REQUEST_TYPE mRequestType; - // Flag that indicates if a request is underway. - private boolean mInProgress; - ... - @Override - protected void onCreate(Bundle savedInstanceState) { - ... - // Start with the request flag set to false - mInProgress = false; - ... - } - ... - /** - * Start a request for geofence monitoring by calling - * LocationClient.connect(). - */ - public void addGeofences() { - // Start a request to add geofences - mRequestType = ADD; - /* - * Test for Google Play services after setting the request type. - * If Google Play services isn't present, the proper request - * can be restarted. - */ - if (!servicesConnected()) { - return; - } - /* - * Create a new location client object. Since the current - * activity class implements ConnectionCallbacks and - * OnConnectionFailedListener, pass the current activity object - * as the listener for both parameters - */ - mLocationClient = new LocationClient(this, this, this) - // If a request is not already underway - if (!mInProgress) { - // Indicate that a request is underway - mInProgress = true; - // Request a connection from the client to Location Services - mLocationClient.connect(); - } else { - /* - * A request is already underway. You can handle - * this situation by disconnecting the client, - * re-setting the flag, and then re-trying the - * request. - */ - } - } - ... -} -</pre> -<h4>Send a request to add the geofences</h4> -<p> - In your implementation of -<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">ConnectionCallbacks.onConnected()</a></code>, - call -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#addGeofences(java.util.List<com.google.android.gms.location.Geofence>, android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener)">LocationClient.addGeofences()</a></code>. - Notice that if the connection fails, -<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code> - isn't called, and the request stops. -</p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - /* - * Provide the implementation of ConnectionCallbacks.onConnected() - * Once the connection is available, send a request to add the - * Geofences - */ - @Override - private void onConnected(Bundle dataBundle) { - ... - switch (mRequestType) { - case ADD : - // Get the PendingIntent for the request - mTransitionPendingIntent = - getTransitionPendingIntent(); - // Send a request to add the current geofences - mLocationClient.addGeofences( - mCurrentGeofences, pendingIntent, this); - ... + private PendingIntent getGeofencePendingIntent() { + // Reuse the PendingIntent if we already have it. + if (mGeofencePendingIntent != null) { + return mGeofencePendingIntent; } + Intent intent = new Intent(this, GeofenceTransitionsIntentService.class); + // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when + // calling addGeofences() and removeGeofences(). + return PendingIntent.getService(this, 0, intent, PendingIntent. + FLAG_UPDATE_CURRENT); } - ... -} </pre> + +<h3>Add geofences</h3> + <p> - Notice that -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#addGeofences(java.util.List<com.google.android.gms.location.Geofence>, android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener)">addGeofences()</a></code> - returns immediately, but the status of the request is indeterminate until Location Services - calls -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html#onAddGeofencesResult(int, java.lang.String[])">onAddGeofencesResult()</a></code> - Once this method is called, you can determine if the request was successful or not. -</p> -<h4>Check the result returned by Location Services</h4> -<p> - When Location Services invokes your implementation of the callback method -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html#onAddGeofencesResult(int, java.lang.String[])">onAddGeofencesResult()</a></code>, - indicating that the request is complete, examine the incoming status code. If the request - was successful, the geofences you requested are active. If the request was unsuccessful, - the geofences aren't active, and you need to re-try the request or report an error. For example: -</p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - /* - * Provide the implementation of - * OnAddGeofencesResultListener.onAddGeofencesResult. - * Handle the result of adding the geofences - * - */ - @Override - public void onAddGeofencesResult( - int statusCode, String[] geofenceRequestIds) { - // If adding the geofences was successful - if (LocationStatusCodes.SUCCESS == statusCode) { - /* - * Handle successful addition of geofences here. - * You can send out a broadcast intent or update the UI. - * geofences into the Intent's extended data. - */ - } else { - // If adding the geofences failed - /* - * Report errors here. - * You can log the error using Log.e() or update - * the UI. - */ - } - // Turn off the in progress flag and disconnect the client - mInProgress = false; - mLocationClient.disconnect(); - } - ... -} -</pre> -<!-- Handle disconnections --> -<h3>Handle disconnections</h3> -<p> - In some cases, Location Services may disconnect from the activity recognition client before - you call -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#disconnect()">disconnect()</a></code>. - To handle this situation, implement <code> -<a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onDisconnected()">onDisconnected()</a></code>. - In this method, set the request flag to indicate that a request is not in progress, and - delete the client: -</p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - /* - * Implement ConnectionCallbacks.onDisconnected() - * Called by Location Services once the location client is - * disconnected. - */ - @Override - public void onDisconnected() { - // Turn off the request flag - mInProgress = false; - // Destroy the current location client - mLocationClient = null; - } - ... -} -</pre> -<!-- Handle connection errors --> -<h3>Handle connection errors</h3> -<p> - Besides handling the normal callbacks from Location Services, you have to provide a callback - method that Location Services calls if a connection error occurs. This callback method - can re-use the {@link android.support.v4.app.DialogFragment} class that you defined to - handle the check for Google Play services. It can also re-use the override you defined - for {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} that - receives any Google Play services results that occur when the user interacts with the - error dialog. The following snippet shows you a sample implementation of the callback method: + To add geofences, use the <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingApi.html#addGeofences(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.location.GeofencingRequest, android.app.PendingIntent)">{@code GeoencingApi.addGeofences()}</a></code> method. + Provide the Google API client, the <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest"> + GeofencingRequest</a></code> object, and the {@link android.app.PendingIntent}. + The following snippet, which processes the results in <code><a href="{@docRoot}reference/com/google/android/gms/common/api/ResultCallback.html#onResult(R)"> + onResult()</a></code>, assumes that the main activity implements <code><a href="{@docRoot}reference/com/google/android/gms/common/api/ResultCallback.html"> + ResultCallback</a></code>: </p> <pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - // Implementation of OnConnectionFailedListener.onConnectionFailed - @Override - public void onConnectionFailed(ConnectionResult connectionResult) { - // Turn off the request flag - mInProgress = false; - /* - * If the error has a resolution, start a Google Play services - * activity to resolve it. - */ - if (connectionResult.hasResolution()) { - try { - connectionResult.startResolutionForResult( - this, - CONNECTION_FAILURE_RESOLUTION_REQUEST); - } catch (SendIntentException e) { - // Log the error - e.printStackTrace(); - } - // If no resolution is available, display an error dialog - } else { - // Get the error code - int errorCode = connectionResult.getErrorCode(); - // Get the error dialog from Google Play services - Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog( - errorCode, - this, - CONNECTION_FAILURE_RESOLUTION_REQUEST); - // If Google Play services can provide an error dialog - if (errorDialog != null) { - // Create a new DialogFragment for the error dialog - ErrorDialogFragment errorFragment = - new ErrorDialogFragment(); - // Set the dialog in the DialogFragment - errorFragment.setDialog(errorDialog); - // Show the error dialog in the DialogFragment - errorFragment.show( - getSupportFragmentManager(), - "Geofence Detection"); - } - } - } +public class MainActivity extends FragmentActivity { ... -} + LocationServices.GeofencingApi.addGeofences( + mGoogleApiClient, + getGeofencingRequest(), + getGeofencePendingIntent() + ).setResultCallback(this); </pre> -<!-- - Handle Geofence Transitions - --> + + <h2 id="HandleGeofenceTransitions">Handle Geofence Transitions</h2> <p> When Location Services detects that the user has entered or exited a geofence, it sends out the {@link android.content.Intent} contained in the {@link android.app.PendingIntent} - you included in the request to add geofences. This {@link android.content.Intent} is + you included in the request to add geofences. This {@link android.content.Intent} is received + by a service like <code>GeofenceTransitionsIntentService</code>, + which obtains the geofencing event from the intent, determines the type of Geofence transition(s), + and determines which of the defined geofences was triggered. It then sends a notification as + the output. </p> -<h3>Define an IntentService</h3> <p> The following snippet shows how to define an {@link android.app.IntentService} that posts a notification when a geofence transition occurs. When the user clicks the notification, the app's main activity appears: </p> <pre> -public class ReceiveTransitionsIntentService extends IntentService { - ... - /** - * Sets an identifier for the service - */ - public ReceiveTransitionsIntentService() { - super("ReceiveTransitionsIntentService"); - } - /** - * Handles incoming intents - *@param intent The Intent sent by Location Services. This - * Intent is provided - * to Location Services (inside a PendingIntent) when you call - * addGeofences() - */ - @Override +public class GeofenceTransitionsIntentService extends IntentService { + ... protected void onHandleIntent(Intent intent) { - // First check for errors - if (LocationClient.hasError(intent)) { - // Get the error code with a static method - int errorCode = LocationClient.getErrorCode(intent); - // Log the error - Log.e("ReceiveTransitionsIntentService", - "Location Services error: " + - Integer.toString(errorCode)); - /* - * You can also send the error code to an Activity or - * Fragment with a broadcast Intent - */ - /* - * If there's no error, get the transition type and the IDs - * of the geofence or geofences that triggered the transition - */ - } else { - // Get the type of transition (entry or exit) - int transitionType = - LocationClient.getGeofenceTransition(intent); - // Test that a valid transition was reported - if ( - (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER) - || - (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) - ) { - List <Geofence> triggerList = - getTriggeringGeofences(intent); + GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent); + if (geofencingEvent.hasError()) { + String errorMessage = GeofenceErrorMessages.getErrorString(this, + geofencingEvent.getErrorCode()); + Log.e(TAG, errorMessage); + return; + } - String[] triggerIds = new String[geofenceList.size()]; + // Get the transition type. + int geofenceTransition = geofencingEvent.getGeofenceTransition(); + + // Test that the reported transition was of interest. + if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || + geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) { + + // Get the geofences that were triggered. A single event can trigger + // multiple geofences. + List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences(); + + // Get the transition details as a String. + String geofenceTransitionDetails = getGeofenceTransitionDetails( + this, + geofenceTransition, + triggeringGeofences + ); - for (int i = 0; i < triggerIds.length; i++) { - // Store the Id of each geofence - triggerIds[i] = triggerList.get(i).getRequestId(); - } - /* - * At this point, you can store the IDs for further use - * display them, or display the details associated with - * them. - */ - } - // An invalid transition was reported + // Send notification and log the transition details. + sendNotification(geofenceTransitionDetails); + Log.i(TAG, geofenceTransitionDetails); } else { - Log.e("ReceiveTransitionsIntentService", - "Geofence transition error: " + - Integer.toString()transitionType)); + // Log the error. + Log.e(TAG, getString(R.string.geofence_transition_invalid_type, + geofenceTransition)); } } - ... -} -</pre> -<!-- Specify the IntentService in the manifest --> -<h3>Specify the IntentService in the manifest</h3> -<p> - To identify the {@link android.app.IntentService} to the system, add a - <code><a href="{@docRoot}guide/topics/manifest/service-element.html"><service></a></code> - element to the app manifest. For example: -</p> -<pre> -<service - android:name="com.example.android.location.ReceiveTransitionsIntentService" - android:label="@string/app_name" - android:exported="false"> -</service> </pre> -<p> - Notice that you don't have to specify intent filters for the service, because it only receives - explicit intents. How the incoming geofence transition intents are created is described in the - section <a href="#requestmonitoring">Send the monitoring request</a>. -</p> + +<p>After detecting the transition event via the {@link android.app.PendingIntent}, + this {@link android.app.IntentService} gets the geofence transition type and tests whether + it is one of the events the app uses to trigger notifications -- either + <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">GEOFENCE_TRANSITION_ENTER</a></code> + or <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_EXIT">GEOFENCE_TRANSITION_EXIT</a></code> + in this case. The service then sends a notification and logs the transition details.</p> <!-- Remove Geofences --> <h2 id="StopGeofenceMonitoring">Stop Geofence Monitoring</h2> -<p> - To stop geofence monitoring, you remove the geofences themselves. You can remove a specific - set of geofences or all the geofences associated with a {@link android.app.PendingIntent}. The - procedure is similar to adding geofences. The first operation gets a location - client for the removal request, and the second makes the request using the client. -</p> -<p> - The callback methods that Location Services invokes when it has finished removing geofences - are defined in the interface -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html">LocationClient.OnRemoveGeofencesResultListener</a></code>. Declare - this interface as part of your class definition, and then add definitions for its two methods: -</p> -<dl> - <dt> -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByPendingIntentResult(int, android.app.PendingIntent)">onRemoveGeofencesByPendingIntentResult()</a></code> - </dt> - <dd> - Callback invoked when Location Services finishes a request to remove all geofences made - by the method -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeGeofences(android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener)">removeGeofences(PendingIntent, LocationClient.OnRemoveGeofencesResultListener)</a></code>. - </dd> - <dt> -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByRequestIdsResult(int, java.lang.String[])">onRemoveGeofencesByRequestIdsResult(List<String>, LocationClient.OnRemoveGeofencesResultListener)</a></code> - </dt> - <dd> - Callback invoked when Location Services finished a request to remove a set of geofences, - specified by their geofence IDs, by the method -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeGeofences(java.util.List<java.lang.String>, com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener)">removeGeofences(List<String>, LocationClient.OnRemoveGeofencesResultListener)</a></code>. - </dd> -</dl> -<p> - Examples of implementing these methods are shown in the next snippets. -</p> -<h3>Remove all geofences</h3> -<p> - Since removing geofences uses some of the methods you use to add geofences, start by defining - another request type: -</p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - // Enum type for controlling the type of removal requested - public enum REQUEST_TYPE = {ADD, REMOVE_INTENT} - ... -} -</pre> -<p> - Start the removal request by getting a connection to Location Services. If the connection fails, -<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code> isn't called, - and the request stops. The following snippet shows how to start the request: -</p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - /** - * Start a request to remove geofences by calling - * LocationClient.connect() - */ - public void removeGeofences(PendingIntent requestIntent) { - // Record the type of removal request - mRequestType = REMOVE_INTENT; - /* - * Test for Google Play services after setting the request type. - * If Google Play services isn't present, the request can be - * restarted. - */ - if (!servicesConnected()) { - return; - } - // Store the PendingIntent - mGeofenceRequestIntent = requestIntent; - /* - * Create a new location client object. Since the current - * activity class implements ConnectionCallbacks and - * OnConnectionFailedListener, pass the current activity object - * as the listener for both parameters - */ - mLocationClient = new LocationClient(this, this, this); - // If a request is not already underway - if (!mInProgress) { - // Indicate that a request is underway - mInProgress = true; - // Request a connection from the client to Location Services - mLocationClient.connect(); - } else { - /* - * A request is already underway. You can handle - * this situation by disconnecting the client, - * re-setting the flag, and then re-trying the - * request. - */ - } - } - ... -} -</pre> -<p> - When Location Services invokes the callback method indicating that the connection is open, - make the request to remove all geofences. Disconnect the client after making the request. - For example: -</p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - /** - * Once the connection is available, send a request to remove the - * Geofences. The method signature used depends on which type of - * remove request was originally received. - */ - private void onConnected(Bundle dataBundle) { - /* - * Choose what to do based on the request type set in - * removeGeofences - */ - switch (mRequestType) { - ... - case REMOVE_INTENT : - mLocationClient.removeGeofences( - mGeofenceRequestIntent, this); - break; - ... - } - } - ... -} -</pre> -<p> - Although the call to -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeGeofences(android.app.PendingIntent, com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener)">removeGeofences(PendingIntent, LocationClient.OnRemoveGeofencesResultListener)</a></code> Services calls - returns immediately, the result of the removal request is indeterminate until Location Services - calls -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByPendingIntentResult(int, android.app.PendingIntent)">onRemoveGeofencesByPendingIntentResult()</a></code>. - The following snippet shows how to define this method: -</p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - /** - * When the request to remove geofences by PendingIntent returns, - * handle the result. - * - *@param statusCode the code returned by Location Services - *@param requestIntent The Intent used to request the removal. - */ - @Override - public void onRemoveGeofencesByPendingIntentResult(int statusCode, - PendingIntent requestIntent) { - // If removing the geofences was successful - if (statusCode == LocationStatusCodes.SUCCESS) { - /* - * Handle successful removal of geofences here. - * You can send out a broadcast intent or update the UI. - * geofences into the Intent's extended data. - */ - } else { - // If adding the geocodes failed - /* - * Report errors here. - * You can log the error using Log.e() or update - * the UI. - */ - } - /* - * Disconnect the location client regardless of the - * request status, and indicate that a request is no - * longer in progress - */ - mInProgress = false; - mLocationClient.disconnect(); - } - ... -} -</pre> -<h3>Remove individual geofences</h3> -<p> - The procedure for removing an individual geofence or set of geofences is similar to the - removal of all geofences. To specify the geofences you want remove, add their geofence ID - values to a {@link java.util.List} of String objects. Pass this {@link java.util.List} to a - different definition of {@code removeGeofences} with the appropriate signature. This method - then starts the removal process. -</p> -<p> - Start by adding a request type for removing geofences by a list, and also add a global variable - for storing the list of geofences: -</p> -<pre> - ... - // Enum type for controlling the type of removal requested - public enum REQUEST_TYPE = {ADD, REMOVE_INTENT, REMOVE_LIST} - // Store the list of geofence Ids to remove - String<List> mGeofencesToRemove; -</pre> -<p> - Next, define a list of geofences you want to remove. For example, this snippet removes the -<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html">Geofence</a></code> - defined by the geofence ID "1": -</p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - List<String> listOfGeofences = - Collections.singletonList("1"); - removeGeofences(listOfGeofences); - ... -} -</pre> -<p> - The following snippet defines the {@code removeGeofences()} method: -</p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - /** - * Start a request to remove monitoring by - * calling LocationClient.connect() - * - */ - public void removeGeofences(List<String> geofenceIds) { - // If Google Play services is unavailable, exit - // Record the type of removal request - mRequestType = REMOVE_LIST; - /* - * Test for Google Play services after setting the request type. - * If Google Play services isn't present, the request can be - * restarted. - */ - if (!servicesConnected()) { - return; - } - // Store the list of geofences to remove - mGeofencesToRemove = geofenceIds; - /* - * Create a new location client object. Since the current - * activity class implements ConnectionCallbacks and - * OnConnectionFailedListener, pass the current activity object - * as the listener for both parameters - */ - mLocationClient = new LocationClient(this, this, this); - // If a request is not already underway - if (!mInProgress) { - // Indicate that a request is underway - mInProgress = true; - // Request a connection from the client to Location Services - mLocationClient.connect(); - } else { - /* - * A request is already underway. You can handle - * this situation by disconnecting the client, - * re-setting the flag, and then re-trying the - * request. - */ - } - } - ... -} -</pre> -<p> - When Location Services invokes the callback method indicating that the connection is open, - make the request to remove the list of geofences. Disconnect the client after making the request. - For example: + +<p>Stopping geofence monitoring when it is no longer needed or desired can help save battery + power and CPU cycles on the device. You can stop geofence monitoring + in the main activity used to add and remove geofences; removing a geofence stops it + immediately. The API provides methods to + remove geofences either by request IDs, or by removing geofences associated with a given + {@link android.app.PendingIntent}. </p> -<pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - private void onConnected(Bundle dataBundle) { - ... - switch (mRequestType) { - ... - // If removeGeofencesById was called - case REMOVE_LIST : - mLocationClient.removeGeofences( - mGeofencesToRemove, this); - break; - ... - } - ... - } - ... -} -</pre> <p> - Define an implementation of -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html#onRemoveGeofencesByRequestIdsResult(int, java.lang.String[])">onRemoveGeofencesByRequestIdsResult()</a></code>. - Location Services invokes this callback method to indicate that the request to remove a list of - geofences is complete. In this method, examine the incoming status code and take the - appropriate action: + The following snippet removes geofences by {@link android.app.PendingIntent}, stopping all + further notification when the device enters or exits previously added geofences: </p> <pre> -public class MainActivity extends FragmentActivity implements - ConnectionCallbacks, - OnConnectionFailedListener, - OnAddGeofencesResultListener { - ... - /** - * When the request to remove geofences by IDs returns, handle the - * result. - * - * @param statusCode The code returned by Location Services - * @param geofenceRequestIds The IDs removed - */ - @Override - public void onRemoveGeofencesByRequestIdsResult( - int statusCode, String[] geofenceRequestIds) { - // If removing the geocodes was successful - if (LocationStatusCodes.SUCCESS == statusCode) { - /* - * Handle successful removal of geofences here. - * You can send out a broadcast intent or update the UI. - * geofences into the Intent's extended data. - */ - } else { - // If removing the geofences failed - /* - * Report errors here. - * You can log the error using Log.e() or update - * the UI. - */ - } - // Indicate that a request is no longer in progress - mInProgress = false; - // Disconnect the location client - mLocationClient.disconnect(); - } - ... +LocationServices.GeofencingApi.removeGeofences( + mGoogleApiClient, + // This is the same pending intent that was used in addGeofences(). + getGeofencePendingIntent() + ).setResultCallback(this); // Result processed in onResult(). } </pre> + <p> - You can combine geofencing with other location-aware features, such as periodic location updates - or activity recognition, which are described in other lessons in this class. -</p> -<p> - The next lesson, - <a href="activity-recognition.html">Recognizing the User's Current Activity</a>, shows you how - to request and receive activity updates. At regular intervals, Location Services can send you - information about the user's current physical activity. Based on this information, you can - change your app's behavior; for example, you can switch to a longer update interval if you - detect that the user is walking instead of driving. + You can combine geofencing with other location-aware features, such as periodic location updates. + For more information, see the other lessons in this class. </p> diff --git a/docs/html/training/location/index.jd b/docs/html/training/location/index.jd index 35e177f..c4dec99 100644 --- a/docs/html/training/location/index.jd +++ b/docs/html/training/location/index.jd @@ -81,4 +81,10 @@ startpage=true Learn how to convert a location's latitude and longitude into an address (reverse geocoding). </dd> + <dt> + <b><a href="geofencing.html">Creating and Monitoring Geofences</a></b> + </dt> <dd> + Learn how to define one or more geographic areas as locations of interest, + called geofences, and detect when the user is close to or inside a geofence. + </dd> </dl> diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 89e72f1..11ae1a6 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -736,6 +736,10 @@ include the action bar on devices running Android 2.1 or higher." Displaying a Location Address </a> </li> + <li><a href="<?cs var:toroot ?>training/location/geofencing.html"> + Creating and Monitoring Geofences + </a> + </li> </ul> </li> </ul> |