1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.geofencing; 18 19 import static com.example.android.geofencing.Constants.ANDROID_BUILDING_ID; 20 import static com.example.android.geofencing.Constants.ANDROID_BUILDING_LATITUDE; 21 import static com.example.android.geofencing.Constants.ANDROID_BUILDING_LONGITUDE; 22 import static com.example.android.geofencing.Constants.ANDROID_BUILDING_RADIUS_METERS; 23 import static com.example.android.geofencing.Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST; 24 import static com.example.android.geofencing.Constants.GEOFENCE_EXPIRATION_TIME; 25 import static com.example.android.geofencing.Constants.TAG; 26 import static com.example.android.geofencing.Constants.YERBA_BUENA_ID; 27 import static com.example.android.geofencing.Constants.YERBA_BUENA_LATITUDE; 28 import static com.example.android.geofencing.Constants.YERBA_BUENA_LONGITUDE; 29 import static com.example.android.geofencing.Constants.YERBA_BUENA_RADIUS_METERS; 30 31 import android.app.Activity; 32 import android.app.PendingIntent; 33 import android.content.Intent; 34 import android.content.IntentSender; 35 import android.os.Bundle; 36 import android.util.Log; 37 import android.widget.Toast; 38 39 import com.google.android.gms.common.ConnectionResult; 40 import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks; 41 import com.google.android.gms.common.GooglePlayServicesUtil; 42 import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; 43 import com.google.android.gms.location.Geofence; 44 import com.google.android.gms.location.LocationClient; 45 import com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener; 46 import com.google.android.gms.location.LocationStatusCodes; 47 48 import java.util.ArrayList; 49 import java.util.List; 50 51 public class MainActivity extends Activity implements ConnectionCallbacks, 52 OnConnectionFailedListener, OnAddGeofencesResultListener { 53 54 // Internal List of Geofence objects. In a real app, these might be provided by an API based on 55 // locations within the user's proximity. 56 List<Geofence> mGeofenceList; 57 58 // These will store hard-coded geofences in this sample app. 59 private SimpleGeofence mAndroidBuildingGeofence; 60 private SimpleGeofence mYerbaBuenaGeofence; 61 62 // Persistent storage for geofences. 63 private SimpleGeofenceStore mGeofenceStorage; 64 65 private LocationClient mLocationClient; 66 // Stores the PendingIntent used to request geofence monitoring. 67 private PendingIntent mGeofenceRequestIntent; 68 69 // Defines the allowable request types (in this example, we only add geofences). 70 private enum REQUEST_TYPE {ADD} 71 private REQUEST_TYPE mRequestType; 72 // Flag that indicates if a request is underway. 73 private boolean mInProgress; 74 75 76 @Override onCreate(Bundle savedInstanceState)77 protected void onCreate(Bundle savedInstanceState) { 78 super.onCreate(savedInstanceState); 79 // Rather than displayng this activity, simply display a toast indicating that the geofence 80 // service is being created. This should happen in less than a second. 81 Toast.makeText(this, getString(R.string.start_geofence_service), Toast.LENGTH_SHORT).show(); 82 83 // Instantiate a new geofence storage area. 84 mGeofenceStorage = new SimpleGeofenceStore(this); 85 // Instantiate the current List of geofences. 86 mGeofenceList = new ArrayList<Geofence>(); 87 // Start with the request flag set to false. 88 mInProgress = false; 89 90 createGeofences(); 91 addGeofences(); 92 93 finish(); 94 } 95 96 /** 97 * In this sample, the geofences are predetermined and are hard-coded here. A real app might 98 * dynamically create geofences based on the user's location. 99 */ createGeofences()100 public void createGeofences() { 101 // Create internal "flattened" objects containing the geofence data. 102 mAndroidBuildingGeofence = new SimpleGeofence( 103 ANDROID_BUILDING_ID, // geofenceId. 104 ANDROID_BUILDING_LATITUDE, 105 ANDROID_BUILDING_LONGITUDE, 106 ANDROID_BUILDING_RADIUS_METERS, 107 GEOFENCE_EXPIRATION_TIME, 108 Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT 109 ); 110 mYerbaBuenaGeofence = new SimpleGeofence( 111 YERBA_BUENA_ID, // geofenceId. 112 YERBA_BUENA_LATITUDE, 113 YERBA_BUENA_LONGITUDE, 114 YERBA_BUENA_RADIUS_METERS, 115 GEOFENCE_EXPIRATION_TIME, 116 Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT 117 ); 118 119 // Store these flat versions in SharedPreferences and add them to the geofence list. 120 mGeofenceStorage.setGeofence(ANDROID_BUILDING_ID, mAndroidBuildingGeofence); 121 mGeofenceStorage.setGeofence(YERBA_BUENA_ID, mYerbaBuenaGeofence); 122 mGeofenceList.add(mAndroidBuildingGeofence.toGeofence()); 123 mGeofenceList.add(mYerbaBuenaGeofence.toGeofence()); 124 } 125 126 /** 127 * Start a request for geofence monitoring by calling LocationClient.connect(). 128 */ addGeofences()129 public void addGeofences() { 130 // Start a request to add geofences. 131 mRequestType = REQUEST_TYPE.ADD; 132 // Test for Google Play services after setting the request type. 133 if (!isGooglePlayServicesAvailable()) { 134 Log.e(TAG, "Unable to add geofences - Google Play services unavailable."); 135 return; 136 } 137 // Create a new location client object. Since this activity class implements 138 // ConnectionCallbacks and OnConnectionFailedListener, it can be used as the listener for 139 // both parameters. 140 mLocationClient = new LocationClient(this, this, this); 141 // If a request is not already underway. 142 if (!mInProgress) { 143 // Indicate that a request is underway. 144 mInProgress = true; 145 // Request a connection from the client to Location Services. 146 mLocationClient.connect(); 147 // A request is already underway, so disconnect the client and retry the request. 148 } else { 149 mLocationClient.disconnect(); 150 mLocationClient.connect(); 151 } 152 } 153 154 @Override onConnectionFailed(ConnectionResult connectionResult)155 public void onConnectionFailed(ConnectionResult connectionResult) { 156 mInProgress = false; 157 // If the error has a resolution, start a Google Play services activity to resolve it. 158 if (connectionResult.hasResolution()) { 159 try { 160 connectionResult.startResolutionForResult(this, 161 CONNECTION_FAILURE_RESOLUTION_REQUEST); 162 } catch (IntentSender.SendIntentException e) { 163 Log.e(TAG, "Exception while resolving connection error.", e); 164 } 165 } else { 166 int errorCode = connectionResult.getErrorCode(); 167 Log.e(TAG, "Connection to Google Play services failed with error code " + errorCode); 168 } 169 } 170 171 /** 172 * Called by Location Services if the location client disconnects. 173 */ 174 @Override onDisconnected()175 public void onDisconnected() { 176 // Turn off the request flag. 177 mInProgress = false; 178 // Destroy the current location client. 179 mLocationClient = null; 180 } 181 182 /** 183 * Once the connection is available, send a request to add the Geofences. 184 */ 185 @Override onConnected(Bundle connectionHint)186 public void onConnected(Bundle connectionHint) { 187 // Use mRequestType to determine what action to take. Only ADD is used in this sample. 188 if (REQUEST_TYPE.ADD == mRequestType) { 189 // Get the PendingIntent for the geofence monitoring request. 190 mGeofenceRequestIntent = getGeofenceTransitionPendingIntent(); 191 // Send a request to add the current geofences. 192 mLocationClient.addGeofences(mGeofenceList, mGeofenceRequestIntent, this); 193 } 194 } 195 196 /** 197 * Called when request to add geofences is complete, with a result status code. 198 */ 199 @Override onAddGeofencesResult(int statusCode, String[] geofenceRequestIds)200 public void onAddGeofencesResult(int statusCode, String[] geofenceRequestIds) { 201 // Log if adding the geofences was successful. 202 if (LocationStatusCodes.SUCCESS == statusCode) { 203 if (Log.isLoggable(TAG, Log.DEBUG)) { 204 Log.d(TAG, "Added geofences successfully."); 205 } 206 } else { 207 Log.e(TAG, "Failed to add geofences. Status code: " + statusCode); 208 } 209 // Turn off the in progress flag and disconnect the client. 210 mInProgress = false; 211 mLocationClient.disconnect(); 212 } 213 214 /** 215 * Checks if Google Play services is available. 216 * @return true if it is. 217 */ isGooglePlayServicesAvailable()218 private boolean isGooglePlayServicesAvailable() { 219 int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); 220 if (ConnectionResult.SUCCESS == resultCode) { 221 if (Log.isLoggable(TAG, Log.DEBUG)) { 222 Log.d(TAG, "Google Play services is available."); 223 } 224 return true; 225 } else { 226 Log.e(TAG, "Google Play services is unavailable."); 227 return false; 228 } 229 } 230 231 /** 232 * Create a PendingIntent that triggers GeofenceTransitionIntentService when a geofence 233 * transition occurs. 234 */ getGeofenceTransitionPendingIntent()235 private PendingIntent getGeofenceTransitionPendingIntent() { 236 Intent intent = new Intent(this, GeofenceTransitionsIntentService.class); 237 return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 238 } 239 240 } 241