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.wearable.geofencing; 18 19 import static com.example.android.wearable.geofencing.Constants.ANDROID_BUILDING_ID; 20 import static com.example.android.wearable.geofencing.Constants.ANDROID_BUILDING_LATITUDE; 21 import static com.example.android.wearable.geofencing.Constants.ANDROID_BUILDING_LONGITUDE; 22 import static com.example.android.wearable.geofencing.Constants.ANDROID_BUILDING_RADIUS_METERS; 23 import static com.example.android.wearable.geofencing.Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST; 24 import static com.example.android.wearable.geofencing.Constants.GEOFENCE_EXPIRATION_TIME; 25 import static com.example.android.wearable.geofencing.Constants.TAG; 26 import static com.example.android.wearable.geofencing.Constants.YERBA_BUENA_ID; 27 import static com.example.android.wearable.geofencing.Constants.YERBA_BUENA_LATITUDE; 28 import static com.example.android.wearable.geofencing.Constants.YERBA_BUENA_LONGITUDE; 29 import static com.example.android.wearable.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.api.GoogleApiClient.ConnectionCallbacks; 41 import com.google.android.gms.common.GooglePlayServicesUtil; 42 import com.google.android.gms.common.api.GoogleApiClient; 43 import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; 44 import com.google.android.gms.location.Geofence; 45 import com.google.android.gms.location.LocationServices; 46 47 import java.util.ArrayList; 48 import java.util.List; 49 50 public class MainActivity extends Activity implements ConnectionCallbacks, 51 OnConnectionFailedListener { 52 53 // Internal List of Geofence objects. In a real app, these might be provided by an API based on 54 // locations within the user's proximity. 55 List<Geofence> mGeofenceList; 56 57 // These will store hard-coded geofences in this sample app. 58 private SimpleGeofence mAndroidBuildingGeofence; 59 private SimpleGeofence mYerbaBuenaGeofence; 60 61 // Persistent storage for geofences. 62 private SimpleGeofenceStore mGeofenceStorage; 63 64 private LocationServices mLocationService; 65 // Stores the PendingIntent used to request geofence monitoring. 66 private PendingIntent mGeofenceRequestIntent; 67 private GoogleApiClient mApiClient; 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 73 @Override onCreate(Bundle savedInstanceState)74 protected void onCreate(Bundle savedInstanceState) { 75 super.onCreate(savedInstanceState); 76 // Rather than displayng this activity, simply display a toast indicating that the geofence 77 // service is being created. This should happen in less than a second. 78 if (!isGooglePlayServicesAvailable()) { 79 Log.e(TAG, "Google Play services unavailable."); 80 finish(); 81 return; 82 } 83 84 mApiClient = new GoogleApiClient.Builder(this) 85 .addApi(LocationServices.API) 86 .addConnectionCallbacks(this) 87 .addOnConnectionFailedListener(this) 88 .build(); 89 90 mApiClient.connect(); 91 92 // Instantiate a new geofence storage area. 93 mGeofenceStorage = new SimpleGeofenceStore(this); 94 // Instantiate the current List of geofences. 95 mGeofenceList = new ArrayList<Geofence>(); 96 createGeofences(); 97 } 98 99 /** 100 * In this sample, the geofences are predetermined and are hard-coded here. A real app might 101 * dynamically create geofences based on the user's location. 102 */ createGeofences()103 public void createGeofences() { 104 // Create internal "flattened" objects containing the geofence data. 105 mAndroidBuildingGeofence = new SimpleGeofence( 106 ANDROID_BUILDING_ID, // geofenceId. 107 ANDROID_BUILDING_LATITUDE, 108 ANDROID_BUILDING_LONGITUDE, 109 ANDROID_BUILDING_RADIUS_METERS, 110 GEOFENCE_EXPIRATION_TIME, 111 Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT 112 ); 113 mYerbaBuenaGeofence = new SimpleGeofence( 114 YERBA_BUENA_ID, // geofenceId. 115 YERBA_BUENA_LATITUDE, 116 YERBA_BUENA_LONGITUDE, 117 YERBA_BUENA_RADIUS_METERS, 118 GEOFENCE_EXPIRATION_TIME, 119 Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT 120 ); 121 122 // Store these flat versions in SharedPreferences and add them to the geofence list. 123 mGeofenceStorage.setGeofence(ANDROID_BUILDING_ID, mAndroidBuildingGeofence); 124 mGeofenceStorage.setGeofence(YERBA_BUENA_ID, mYerbaBuenaGeofence); 125 mGeofenceList.add(mAndroidBuildingGeofence.toGeofence()); 126 mGeofenceList.add(mYerbaBuenaGeofence.toGeofence()); 127 } 128 129 130 @Override onConnectionFailed(ConnectionResult connectionResult)131 public void onConnectionFailed(ConnectionResult connectionResult) { 132 // If the error has a resolution, start a Google Play services activity to resolve it. 133 if (connectionResult.hasResolution()) { 134 try { 135 connectionResult.startResolutionForResult(this, 136 CONNECTION_FAILURE_RESOLUTION_REQUEST); 137 } catch (IntentSender.SendIntentException e) { 138 Log.e(TAG, "Exception while resolving connection error.", e); 139 } 140 } else { 141 int errorCode = connectionResult.getErrorCode(); 142 Log.e(TAG, "Connection to Google Play services failed with error code " + errorCode); 143 } 144 } 145 146 /** 147 * Once the connection is available, send a request to add the Geofences. 148 */ 149 @Override onConnected(Bundle connectionHint)150 public void onConnected(Bundle connectionHint) { 151 // Get the PendingIntent for the geofence monitoring request. 152 // Send a request to add the current geofences. 153 mGeofenceRequestIntent = getGeofenceTransitionPendingIntent(); 154 LocationServices.GeofencingApi.addGeofences(mApiClient, mGeofenceList, 155 mGeofenceRequestIntent); 156 Toast.makeText(this, getString(R.string.start_geofence_service), Toast.LENGTH_SHORT).show(); 157 finish(); 158 } 159 160 @Override onConnectionSuspended(int i)161 public void onConnectionSuspended(int i) { 162 if (null != mGeofenceRequestIntent) { 163 LocationServices.GeofencingApi.removeGeofences(mApiClient, mGeofenceRequestIntent); 164 } 165 } 166 167 168 /** 169 * Checks if Google Play services is available. 170 * @return true if it is. 171 */ isGooglePlayServicesAvailable()172 private boolean isGooglePlayServicesAvailable() { 173 int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); 174 if (ConnectionResult.SUCCESS == resultCode) { 175 if (Log.isLoggable(TAG, Log.DEBUG)) { 176 Log.d(TAG, "Google Play services is available."); 177 } 178 return true; 179 } else { 180 Log.e(TAG, "Google Play services is unavailable."); 181 return false; 182 } 183 } 184 185 /** 186 * Create a PendingIntent that triggers GeofenceTransitionIntentService when a geofence 187 * transition occurs. 188 */ getGeofenceTransitionPendingIntent()189 private PendingIntent getGeofenceTransitionPendingIntent() { 190 Intent intent = new Intent(this, GeofenceTransitionsIntentService.class); 191 return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 192 } 193 194 } 195