Geofence does not get any transition updates, despite setting location 100 miles away,
does not trigger anything even GPS is on.
public class GeofenceManager extends IntentService implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener, LocationClient.OnAddGeofencesResultListener, LocationClient.OnRemoveGeofencesResultListener {
private Context context;
private LocationClient locClient;
private PendingIntent penIntent;
private ArrayList<Geofence> geofenceList = new ArrayList<Geofence>();
private double dLongitude, dLatitude;
private boolean bGeofenceEnable = false;
private BroadcastReceiver receiverGeofence;
/* ===========================================================================================================================
* CONSTRUCTOR -- Checks if Google play service are available
*----------------------------------------------------------------------------------------------------------------------------*/
GeofenceManager(Context context){
super("ReceiveTransitionsIntentService");
this.context = context;
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
if (ConnectionResult.SUCCESS == resultCode) {
bGeofenceEnable = true;
} else {
Log.d(GPSManager.LOG_TAG,"GEO_FENCE: ERROR google play services not available");
}
}
/* ===========================================================================================================================
* METHOD -- using hard coded values for test purpose
*----------------------------------------------------------------------------------------------------------------------------*/
public void setGeofence( double latitude, double longitude){
if(bGeofenceEnable){
dLatitude = latitude = 51.515399;
dLongitude = longitude = -0.144313;
locClient.connect();
}
}
private PendingIntent createRequestPendingIntent() {
if (null != penIntent) {
return penIntent;
} else {
Intent intent = new Intent(context, GeofenceManager.class); // Create an Intent pointing to the IntentService
return PendingIntent.getService( context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
#Override public void onConnected(Bundle bundle) {
Geofence geofence = new Geofence.Builder()
.setRequestId("123")
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_EXIT|Geofence.GEOFENCE_TRANSITION_ENTER ) // exit geo fence listener
.setCircularRegion(dLatitude, dLongitude, 20) // GPS coordinates
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.build();
geofenceList.add(geofence);
penIntent = createRequestPendingIntent();
locClient.addGeofences(geofenceList, penIntent, this);
}
#Override public void onDisconnected() { }
#Override public void onConnectionFailed(ConnectionResult connectionResult) { }
#Override public void onRemoveGeofencesByPendingIntentResult(int i, PendingIntent pendingIntent) { }
#Override public void onAddGeofencesResult(int i, String[] strings) {
if(i == LocationStatusCodes.SUCCESS){
Log.d(GPSManager.LOG_TAG, GPSManager.getTime()+" GEO_FENCE: added");
} else {
Log.d(GPSManager.LOG_TAG, GPSManager.getTime()+" GEO_FENCE: add ERROR "+i);
}
// locClient.disconnect();
}
#Override public void onRemoveGeofencesByRequestIdsResult(int i, String[] strings) {
if(i == LocationStatusCodes.SUCCESS)
Log.d(GPSManager.LOG_TAG, GPSManager.getTime()+" GEO_FENCE: old Removed");
}
#Override protected void onHandleIntent(Intent intent) {
int transition = LocationClient.getGeofenceTransition(intent);
if ((transition == Geofence.GEOFENCE_TRANSITION_ENTER) || (transition == Geofence.GEOFENCE_TRANSITION_EXIT)){
Log.d(GPSManager.LOG_TAG,"GEO_FENCE Transition detected");
Toast.makeText(context, "Geo fence movement detected", Toast.LENGTH_LONG).show();
ToneGenerator toneG = new ToneGenerator(AudioManager.STREAM_ALARM, 100);
toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 1000); // 200 is duration in ms
} else {
Log.d(GPSManager.LOG_TAG,"GEO_FENCE Transition UPDATE");
}
}
}
and in Manifest file I have declared:
<service android:name="com.Utilities.GeofenceManager" android:exported="true" />
also have tried:
<service android:name="com.Utilities.GeofenceManager" android:exported="false" />
<service android:name=".GeofenceManager" android:exported="false" />
Geofence is added as onAddGeofencesResult() method is called with success
Manage to fix the problem, by creating a new class for Geofence transition Intent,
and removed the IntentService code from GeofenceManager class
public class GeofenceIntentService extends IntentService {
public GeofenceIntentService() {
super("GeofenceIntentService");
}
#Override protected void onHandleIntent(Intent intent) {
int transition = LocationClient.getGeofenceTransition(intent);
if ((transition == Geofence.GEOFENCE_TRANSITION_ENTER) || (transition == Geofence.GEOFENCE_TRANSITION_EXIT)){
Log.d(GPSManager.LOG_TAG, "GEO_FENCE "+((transition == Geofence.GEOFENCE_TRANSITION_ENTER) ? "ENTER " : "EXIT") +" Transition detected");
Toast.makeText(this, "Geo fence movement detected", Toast.LENGTH_LONG).show();
ToneGenerator toneG = new ToneGenerator(AudioManager.STREAM_ALARM, 80);
toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE, 1000); // 200 is duration in ms
} else {
Log.d(GPSManager.LOG_TAG,"GEO_FENCE Transition UPDATE");
}
}
}
Related
I am using geofence api for proximity but it is not working properly. some times it gives in range and out range at the same location.
i am also using GPS library to improve accuracy of geofence inrange-outrange but still it is not working properly. i am using below code for geofence :
I am using 300 meters radius for considering inrange-outrange (enter-exit)
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.geofencedemo">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".CheckPermission"
android:screenOrientation="portrait"
android:theme="#style/DialogActivity" />
<receiver
android:name="services.GeofenceTransitionsReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="com.geofencedemo.ACTION_RECEIVE_GEOFENCE" />
<category android:name="com.geofencedemo" />
</intent-filter>
</receiver>
<service
android:name="services.GeofenceTransitionsIntentService"
android:exported="false" />
</application>
MainActivity.java (for registering geofence)
public class MainActivity extends Activity implements com.google.android.gms.location.LocationListener {
private LocationManager locationManager;
private LocationRequest mLocationRequest;
private GoogleApiClient mGoogleApiClient;
private final float GEOFENCE_RADIUS_IN_METERS = 300;//Meter(s)
private final long GEOFENCE_EXPIRATION_IN_MILLISECONDS = Geofence.NEVER_EXPIRE;
private int GEOFENCE_TRANSITION_ENTER = 1;
private int GEOFENCE_TRANSITION_EXIT = 2;
private List<Geofence> mGeofenceList;
private String geofenceID = "testGeofence";
private SharedPreferences preferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
preferences = getSharedPreferences("ISSKEYGEOFENCE-prefs", MODE_PRIVATE);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Button btnSetLocation = (Button) findViewById(R.id.btn_setlocation);
btnSetLocation.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
boolean isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!isGPSEnabled) {
Toast.makeText(getApplicationContext(), "Please enable Location service to allow set location", Toast.LENGTH_LONG).show();
return;
}
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1){
Log.d("myTag", "check marshmallow permission...");
Intent permissionIntent = new Intent(MainActivity.this, CheckPermission.class);
startActivityForResult(permissionIntent, 1111);
} else {
intGoogleAPI();
}
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 1111:
if (resultCode == RESULT_OK) {
intGoogleAPI();
}
break;
}
}
private void intGoogleAPI() {
mLocationRequest = LocationRequest.create();
mLocationRequest.setInterval(3000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setFastestInterval(1000);
mGeofenceList = new ArrayList<Geofence>(1);
mGoogleApiClient = new GoogleApiClient.Builder(MainActivity.this).addApi(LocationServices.API).addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(final Bundle bundle) {
if (isServiceConnected()) {
Log.d("myTag", "request for update current location");
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, MainActivity.this);
}
}
#Override
public void onConnectionSuspended(final int cause) {}
}).addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(final ConnectionResult connectionResult) {}
}).build();
mGoogleApiClient.connect();
}
private boolean isServiceConnected() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(MainActivity.this);
return ConnectionResult.SUCCESS == resultCode;
}
#Override
public void onLocationChanged(Location mLastLocation) {
if (isServiceConnected()) {
Log.d("myTag", "request removed location update");
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}
try {
addLocation(mLastLocation.getLatitude(), mLastLocation.getLongitude());
} catch (Exception e) {}
}
public void addLocation(final double latitude, final double longitude) {
Log.v("myTag", "set lcation lat: " + latitude);
Log.v("myTag", "set lcation lag: " + longitude);
mGeofenceList.add(new Geofence.Builder().setRequestId(geofenceID).setTransitionTypes(GEOFENCE_TRANSITION_ENTER | GEOFENCE_TRANSITION_EXIT).setCircularRegion(latitude, longitude, GEOFENCE_RADIUS_IN_METERS).setExpirationDuration(GEOFENCE_EXPIRATION_IN_MILLISECONDS).build());
final List<String> GEO_FENCE_ID_LIST = new ArrayList<String>();
GEO_FENCE_ID_LIST.add(geofenceID);
if (mGoogleApiClient != null) {
LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient, GEO_FENCE_ID_LIST).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(final Status status) {
if (status.isSuccess()) {
// successfully removed geofence...
Log.d("myTag", "removed old geofence successfully ");
} else {
Log.d("myTag", "Not removed old geofence");
}
}
});
}
if (mGoogleApiClient != null) {
LocationServices.GeofencingApi.addGeofences(mGoogleApiClient, getGeofencingRequest(), getGeofencePendingIntent()).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(final Status status) {
if (status.isSuccess()) {
// successfully Added geofence...
Log.d("myTag", "New geofence successfully added");
Toast.makeText(getApplicationContext(), "Successfully added geofence", Toast.LENGTH_LONG).show();
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("isinrange", false);
editor.putString("lat", String.valueOf(latitude));
editor.putString("log", String.valueOf(longitude));
editor.apply();
}
try {
if (isServiceConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, MainActivity.this);
}
mGoogleApiClient.disconnect();
} catch (Exception e) {}
}
});
}
}
private GeofencingRequest getGeofencingRequest() {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_EXIT);
builder.addGeofences(mGeofenceList);
return builder.build();
}
private PendingIntent getGeofencePendingIntent() {
Intent intent = new Intent("com.geofencedemo.ACTION_RECEIVE_GEOFENCE");
return PendingIntent.getBroadcast(MainActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
GeofenceTransitionsIntentService (Trigger inrange,outrange by geofence)
public class GeofenceTransitionsIntentService extends IntentService implements com.google.android.gms.location.LocationListener {
public GeofenceTransitionsIntentService() {
super("GeofenceTransitionsIntentService");
}
#Override
public void onCreate() {
super.onCreate();
preferences = getSharedPreferences("ISSKEYGEOFENCE-prefs", MODE_PRIVATE);
}
private LocationRequest mLocationRequest;
private GoogleApiClient mGoogleApiClient;
private boolean isLocationFetched = false;
private String geofenceTransitionIds;
private boolean isInRange = false;
private SharedPreferences preferences;
#Override
protected void onHandleIntent(final Intent intent) {
if (intent.getExtras() != null) {
int geofenceTransitionType = intent.getIntExtra("TransitionType", 4);
geofenceTransitionIds = intent.getStringExtra("TransitionId");
/*final String lat = intent.getStringExtra("TransitionId_lat");
final String log = intent.getStringExtra("TransitionId_log");*/
if (geofenceTransitionIds != null && geofenceTransitionIds.length() > 0) {
switch (geofenceTransitionType) {
case Geofence.GEOFENCE_TRANSITION_ENTER:
geofenceTransitionIds(geofenceTransitionIds, true);
//new IntGoogleApiClient(appStorage, null, true).Intialize();
//processIn(geofenceTransitionIds, lat, log);
break;
case Geofence.GEOFENCE_TRANSITION_EXIT:
geofenceTransitionIds(geofenceTransitionIds, false);
//new IntGoogleApiClient(appStorage, null, true).Intialize();
//processOut(geofenceTransitionIds, lat, log);
break;
}
}
}
}
private void geofenceTransitionIds (String geofenceTransitionIds, boolean isEnteCall) {
if (geofenceTransitionIds != null && geofenceTransitionIds.length() > 0) {
isInRange = isEnteCall;
intGoogleAPI();
}
}
private void intGoogleAPI() {
mLocationRequest = LocationRequest.create();
mLocationRequest.setInterval(3000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setFastestInterval(1000);
mGoogleApiClient = new GoogleApiClient.Builder(GeofenceTransitionsIntentService.this).addApi(LocationServices.API).addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(final Bundle bundle) {
if (isServiceConnected()) {
// please consider here location permission is already allowed...
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, GeofenceTransitionsIntentService.this);
}
}
#Override
public void onConnectionSuspended(final int cause) {}
}).addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(final ConnectionResult connectionResult) {}
}).build();
mGoogleApiClient.connect();
}
private boolean isServiceConnected() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(GeofenceTransitionsIntentService.this);
return ConnectionResult.SUCCESS == resultCode;
}
#Override
public void onLocationChanged(Location passedLocation) {
if (passedLocation != null && !isLocationFetched) {
if (isServiceConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, GeofenceTransitionsIntentService.this);
}
isLocationFetched = true;
try {
if (geofenceTransitionIds != null && geofenceTransitionIds.length() > 0) {
if (isInRange) {
processIn(geofenceTransitionIds, String.valueOf(passedLocation.getLatitude()), String.valueOf(passedLocation.getLongitude()));
} else {
processOut(geofenceTransitionIds, String.valueOf(passedLocation.getLatitude()), String.valueOf(passedLocation.getLongitude()));
}
}
} catch (Exception e) {}
disconnectGoogleClient();
}
}
private void disconnectGoogleClient() {
try {
if (mGoogleApiClient != null) {
if (isServiceConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, GeofenceTransitionsIntentService.this);
}
mGoogleApiClient.disconnect();
}
} catch (Exception e) {}
}
private void processIn(String passedIds, String lat, String log) {
if (passedIds != null) {
List<String> items = Arrays.asList(passedIds.split("\\s*,\\s*"));
for (String deviceName : items) {
if (deviceName != null) {
boolean isOutRange = preferences.getBoolean("isinrange", false);
if (mappingRadius(true, deviceName, lat, log) && isOutRange) {
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("isinrange", false);
editor.apply();
sendNotification(GeofenceTransitionsIntentService.this, "In range come!", Color.BLACK);
}
}
}
}
}
private void processOut(String passedIds, String lat, String log) {
if (passedIds != null) {
List<String> items = Arrays.asList(passedIds.split("\\s*,\\s*"));
for (String deviceName : items) {
if (deviceName != null) {
boolean isInRange = preferences.getBoolean("isinrange", false);
if (mappingRadius(false, deviceName, lat, log) && !isInRange) {
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("isinrange", true);
editor.apply();
sendNotification(GeofenceTransitionsIntentService.this, "Out range come!", Color.RED);
}
}
}
}
}
private boolean mappingRadius(boolean isInRange, String deviceName, String currentLat, String currentLog) {
final float cLat = Float.parseFloat(currentLat);
final float cLog = Float.parseFloat(currentLog);
Log.d("myTag", "GeofenceTransitionsReceiver lat " + cLat);
Log.d("myTag", "GeofenceTransitionsReceiver log " + cLog);
float appLat;
float appLog;
appLat = Float.parseFloat(preferences.getString("lat", "0.0"));
appLog = Float.parseFloat(preferences.getString("log", "0.0"));
Log.d("myTag", "GeofenceTransitionsReceiver app lat " + appLat);
Log.d("myTag", "GeofenceTransitionsReceiver app log " + appLog);
Location current_latlog = new Location("crntlocation");
current_latlog.setLatitude(appLat);
current_latlog.setLongitude(appLog);
Location new_latlog = new Location("newlocation");
new_latlog.setLatitude(cLat);
new_latlog.setLongitude(cLog);
final double distance = current_latlog.distanceTo(new_latlog);
Log.v("myTag", "GeofenceTransitionsReceiver calculation distance is: " + distance);
//if (distance < 2000) {
if (isInRange) {
if (distance <= 300) { // with in 300 Meters consider as in range...
return true;
} else {
return false;
}
} else {
if (distance >= 300) { // more than 300 Meters consider as out range...
return true;
} else {
return false;
}
}
/*} else {
return false;
}*/
}
private void sendNotification(final Context context, String notificationMessage, int color) {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addNextIntent(new Intent());
PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(R.mipmap.ic_launcher).setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher)).setContentTitle(context.getResources().getString(R.string.app_name)).setTicker(notificationMessage).setContentText(notificationMessage).setContentIntent(notificationPendingIntent).setColor(color).setVibrate(new long[] { 1000, 1000, 1000, 1000 }).setAutoCancel(true);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify((int) System.currentTimeMillis(), builder.build());
}
}
GeofenceTransitionsReceiver (handling/calculate distance after geofence trigger)
public class GeofenceTransitionsReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
String errorMessage = getErrorString(context, geofencingEvent.getErrorCode());
Log.d("myTag", "GeofenceTransitionsReceiver error " +errorMessage);
// sendNotification(context, errorMessage, Color.RED);
return;
}
int geofenceTransition = geofencingEvent.getGeofenceTransition();
Log.d("myTag", "GeofenceTransitionsReceiver " + geofenceTransition);
// for testing....
//sendNotification(context, context.getResources().getString(R.string.geofence_transition_type, geofenceTransition), Color.RED);
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
String geofenceTransitionIds = getGeofenceTransitionIds(triggeringGeofences);
Location triggeringLocation = geofencingEvent.getTriggeringLocation();
Intent serviceIntent = new Intent(context.getApplicationContext(), GeofenceTransitionsIntentService.class);
serviceIntent.putExtra("TransitionType", geofenceTransition);
serviceIntent.putExtra("TransitionId", geofenceTransitionIds);
serviceIntent.putExtra("TransitionId_lat", String.valueOf(triggeringLocation.getLatitude()));
serviceIntent.putExtra("TransitionId_log", String.valueOf(triggeringLocation.getLongitude()));
context.startService(serviceIntent);
} else {
// sendNotification(context,
// context.getResources().getString(R.string.geofence_transition_invalid_type,
// geofenceTransition), Color.RED);
}
}
private String getErrorString(final Context context, int errorCode) {
Resources mResources = context.getResources();
switch (errorCode) {
case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE:
return "Geofence service is not available now";
case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES:
return "Your app has registered too many geofences";
case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS:
return "You have provided too many PendingIntents to the addGeofences() call";
default:
return "Unknown error: the Geofence service is not available now";
}
}
private String getGeofenceTransitionIds(List<Geofence> triggeringGeofences) {
List<String> triggeringGeofencesIdsList = new ArrayList<String>();
for (Geofence geofence : triggeringGeofences) {
triggeringGeofencesIdsList.add(geofence.getRequestId());
}
return TextUtils.join(",", triggeringGeofencesIdsList);
}
}
Is there anything else we can do to improve accuracy and overcome the problem of inrange outrange at the same location ?
I used Google ActivityRecognitionApi for tracking whether i am walking or not.
But It seems too slow. What's the problem?
It doesn't related to internet connectivity.
[Walking.java]
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_walking);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(ActivityRecognition.API).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
mGoogleApiClient.connect();
}
#Override
public void onConnected(Bundle bundle) {
Intent intent = new Intent(this, ActivityRecognitionService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, 1000, pendingIntent);
}
#Override
public void onConnectionSuspended(int i) {
Toast.makeText(Walking.this, "Hello1", Toast.LENGTH_SHORT).show();
Log.e("Connection Failed : ", "Connection Failed");
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Toast.makeText(Walking.this, "Hello2", Toast.LENGTH_SHORT).show();
Log.e("Connection Failed : ", "Connection Failed");
}
[ActivityRecognitionService.java]
import java.util.List;
public class ActivityRecognitionService extends IntentService {
public static final String ACTION_IntentService = "com.example.android.skywalker.RESPONSE";
public static final String ACTIVITY_RESULT = "RESULT";
public Intent localIntent;
public ActivityRecognitionService() {
super("ActivityRecognitionService");
}
public ActivityRecognitionService(String name) {
super(name);
}
#Override
protected void onHandleIntent(Intent intent) {
if(ActivityRecognitionResult.hasResult(intent)) {
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
handleDetectedActivities(result.getProbableActivities());
}
}
private void handleDetectedActivities(List<DetectedActivity> probableActivities) {
int Flag = 0;
for(DetectedActivity activity : probableActivities) {
if(activity.getType() == DetectedActivity.ON_FOOT) {
Flag = 1;
if (activity.getConfidence() >= 50) Log.e("YES", "YES");
break;
}
}
//Intent intent = new Intent(ActivityRecognitionService.this, Walking.class);
if (Flag == 0) {
//localIntent = new Intent(Walking.BROADCAST_ACTION).putExtra(Walking.RESPONSE_STATUS, "YES");
Log.e("NO", "NO");
}
}
}
I don't think there is any problem with your code, it looks exactly the same as mine.
The normal rate for detection is 3 seconds -> 6 seconds if your "detectionIntervalMillis" value = 0.
But when you switch between 2 different activities (for example: from walking -> in the car....) it will take more, maybe 20s to 40s.
I'm trying to implement Geofence on my APP and the problem is that is not triggering when I'm on the place that I've marked.
The thing that I do is I set the Latitude & Longitude from a Place Picker.
The values that I'm getting are fine because I put it on google maps and the place is the correct one, because I've read many answers that people was setting bad values from lat/long.
I've two classes : GeofenceSingleton.class and GeofenceTransitionsIntentService.
The GeofenceSingleton.class looks like :
public class GeofenceSingleton implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<Status> {
private GoogleApiClient googleApiClient;
private static GeofenceSingleton _singleInstance;
private static Context appContext;
private static final String ERR_MSG = "Application Context is not set!! " +
"Please call GeofenceSngleton.init() with proper application context";
private PendingIntent mGeofencePendingIntent;
private static ArrayList<Geofence> mGeofenceList;
private GeofenceSingleton() {
if (appContext == null)
throw new IllegalStateException(ERR_MSG);
this.googleApiClient = new GoogleApiClient.Builder(this.appContext)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
this.googleApiClient.connect();
mGeofenceList = new ArrayList<Geofence>();
}
/**
* #param applicationContext
*/
public static void init(Context applicationContext) {
appContext = applicationContext;
}
public static GeofenceSingleton getInstance() {
if (_singleInstance == null)
synchronized (GeofenceSingleton.class) {
if (_singleInstance == null)
_singleInstance = new GeofenceSingleton();
}
return _singleInstance;
}
#Override
public void onConnected(Bundle bundle) {
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
public void addGeofence(Location location, String uid) {
mGeofenceList.add(new Geofence.Builder()
.setRequestId(uid)
.setCircularRegion(
location.getLatitude(),
location.getLongitude(),
500
)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
.setExpirationDuration(1000000)
.build());
}
private GeofencingRequest getGeofencingRequest() {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
builder.addGeofences(mGeofenceList);
return builder.build();
}
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(appContext, GeofenceSingleton.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
// addGeofences() and removeGeofences().
return PendingIntent.getService(appContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public void startGeofencing() {
if (!googleApiClient.isConnected()) {
Toast.makeText(appContext, "API client not connected", Toast.LENGTH_SHORT).show();
return;
}
LocationServices.GeofencingApi.addGeofences(
googleApiClient,
// The GeofenceRequest object.
getGeofencingRequest(),
// A pending intent that that is reused when calling removeGeofences(). This
// pending intent is used to generate an intent when a matched geofence
// transition is observed.
getGeofencePendingIntent()
).setResultCallback(this); // Result processed in onResult().
Toast.makeText(appContext,"Geofencing started", Toast.LENGTH_LONG).show();
}
public void removeGeofence(){
LocationServices.GeofencingApi.removeGeofences(
googleApiClient,
// This is the same pending intent that was used in addGeofences().
getGeofencePendingIntent()
);
}
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
// Update state and save in shared preferences.
Toast.makeText(
appContext,
"YO",
Toast.LENGTH_SHORT
).show();
} else {
Toast.makeText(
appContext,
"NOO",
Toast.LENGTH_SHORT
).show();
}
}
}
And GeofenceTransitionsIntentService
public class GeofenceTransitionsIntentService extends IntentService {
private static final String TAG = "Geofence-Service";
private Handler handler;
SharedPreferences sp;
SharedPreferences.Editor editor;
String EmailName, MessageEmail;
Context mContext;
Boolean EmailSent = false;
public GeofenceTransitionsIntentService() {
super(TAG);
}
#Override
public void onCreate() {
super.onCreate();
this.mContext = this;
sp = PreferenceManager.getDefaultSharedPreferences(this);
handler = new Handler();
}
#Override
protected void onHandleIntent(Intent intent) {
final GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
Log.e(TAG, "Error in geofencing event");
return;
}
// Get the transition type.
final int geofenceTransition = geofencingEvent.getGeofenceTransition();
// Test that the reported transition was of interest.
if (geofenceTransition == 1) {
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Entered", Toast.LENGTH_SHORT).show();
sendNotification();
}
});
Log.i(TAG, "Entered");
}
else {
// Log the error.
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Sorry you are out", Toast.LENGTH_SHORT).show();
}
});
Log.e(TAG, String.valueOf(geofenceTransition));
}
}
private void sendNotification() {
Toast.makeText(GeofenceTransitionsIntentService.this, "HEEEEEEEY I'M IN", Toast.LENGTH_SHORT).show();
}
On my MainActivity I've got this :
GeofenceSingleton.init(this); //If I don't call this the class doesn't work
this.geofenceSingleton = GeofenceSingleton.getInstance();
And then I build a Geofence as follows :
Location geofence = new Location("");
geofence.setLatitude(Double.parseDouble(latitude));
geofence.setLongitude(Double.parseDouble(longitude));
geofenceSingleton.addGeofence(geofence, GeofenceKey);
geofenceSingleton.startGeofencing();
NOTES
I've added <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> on my manifest.xml
I declared the service as <service android:name=".GeofenceTransitionsIntentService"/>
And I added this line on my build.gradle : compile 'com.google.android.gms:play-services:7.8.0'
It only shows Toast.makeText(appContext,"YO",Toast.LENGTH_SHORT).show(); that's because status is success, and also shows the Toast.makeText(appContext,"Geofencing started", Toast.LENGTH_LONG).show(); means that it's started.
QUESTION
Why my IntentService is never called?
I tested it putting a Lat/Long of my home and since I'm on it should be triggered, isn't it?
It will never work because what you are doing is
Intent intent = new Intent(appContext, GeofenceSingleton.class);
passing intent to GeofenceSingleton ?
What you need to do is
Intent intent = new Intent(appContext, GeofenceTransitionsIntentService.class);
If your Intent is already constructed with the proper class (see varunkr's answer), you also need to make sure the service is also included in your AndroidManifest.xml.
<application
...
<service android:name=".GeofencingTransitionIntentService" />
Make sure your service and broadcast receive have these property enabled in manifest.
android:enabled="true"
android:exported="true"
I know similar questions have been asked before but answers were not perfect.
I created an app with geofences using the sample code from android developer website. I did not use any shared preferences to store geofences as I am not removing the geofences. I am testing the app from within the geofence, but my smartphone receives notifications every time the app runs and no notifications are observed when the app is killed. Why does this happen? I think I am supposed to receive notifications even when the app is killed.
MainActivity
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawer_layout);
.....
GeofencingTask myTask = new GeofencingTask();
myTask.execute();
}
private class GeofencingTask extends AsyncTask<String,Void,String> implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
#Override
protected void onPreExecute() {
}
#Override
protected String doInBackground(String... params) {
mGoogleApiClient = new GoogleApiClient.Builder(MainActivity.this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
mGeofenceList = new ArrayList<Geofence>();
mGeofenceList.add(new Geofence.Builder()
.setRequestId("1")
.setCircularRegion(
Constants.MyAPP_LOCATION_LATITUDE,
Constants.MyAPP_LOCATION_LONGITUDE,
Constants.MyAPP_RADIUS
)
.setExpirationDuration(Constants.GEOFENCE_EXPIRATION_TIME)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
Geofence.GEOFENCE_TRANSITION_EXIT)
.build());
return null;
}
protected void onPostExecute(String s) {
if (s == null) {
return;
}
}
#Override
public void onConnected(Bundle bundle) {
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
getGeofencingRequest(),
getGeofencePendingIntent()
);
Toast.makeText(MainActivity.this, "Starting gps", Toast.LENGTH_SHORT).show();
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
if (connectionResult.hasResolution()) {
try {
connectionResult.startResolutionForResult(MainActivity.this,
Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST);
} catch (IntentSender.SendIntentException e) {
Log.e(TAG, "Exception while resolving connection error.", e);
}
} else {
int errorCode = connectionResult.getErrorCode();
Log.e(TAG, "Connection to Google Play services failed with error code " + errorCode);
}
}
}
GeofenceTransitionsIntentService.java
public class GeofenceTransitionsIntentService extends IntentService{
String TAG = "GeofenceTransitionsIntentService";
int geofenceTransition;
public GeofenceTransitionsIntentService() {
super("name");
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
protected void onHandleIntent(Intent intent) {
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
int errorCode = geofencingEvent.getErrorCode();
Log.e(TAG, "Location Services error: " + errorCode);
}
geofenceTransition = geofencingEvent.getGeofenceTransition();
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 triggeringGeofences = geofencingEvent.getTriggeringGeofences();
// Get the transition details as a String.
String geofenceTransitionDetails = getGeofenceTransitionDetails(
this,
geofenceTransition,
triggeringGeofences
);
Log.i("GeofenceTransitionDetails",geofenceTransitionDetails);
// Send notification and log the transition details.
sendNotification(geofenceTransitionDetails);
sendInOutsTask myTask = new sendInOutsTask();
myTask.execute();
Log.i(TAG, geofenceTransitionDetails);
} else {
// Log the error.
Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
geofenceTransition));
}
}
private String getGeofenceTransitionDetails(
Context context,
int geofenceTransition,
List<Geofence> triggeringGeofences) {
String geofenceTransitionString = getTransitionString(geofenceTransition);
// Get the Ids of each geofence that was triggered.
ArrayList triggeringGeofencesIdsList = new ArrayList();
for (Geofence geofence : triggeringGeofences) {
triggeringGeofencesIdsList.add(geofence.getRequestId());
}
String triggeringGeofencesIdsString = TextUtils.join(", ", triggeringGeofencesIdsList);
return geofenceTransitionString;
}
/**
* Posts a notification in the notification bar when a transition is detected.
* If the user clicks the notification, control goes to the MainActivity.
*/
private void sendNotification(String notificationDetails) {
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(notificationIntent);
PendingIntent notificationPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.mipmap.zemoso_logo)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),
R.mipmap.zemoso_logo))
.setColor(Color.RED)
.setContentTitle(notificationDetails)
.setContentText(getString(R.string.geofence_transition_notification_text))
.setContentIntent(notificationPendingIntent);
builder.setAutoCancel(true);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(0, builder.build());
}
private String getTransitionString(int transitionType) {
switch (transitionType) {
case Geofence.GEOFENCE_TRANSITION_ENTER:
return Constants.WELCOME_NOTIFICATION;
case Geofence.GEOFENCE_TRANSITION_EXIT:
return Constants.EXIT_NOTIFICATION;
default:
return null;
}
}
}
Ok, May be a bit late but I'll post the answer on how I fixed this issue myself. Two things:
1) I was adding the geofences every time I run the MainActivity and geofences API triggers the geofencing event, if you add the geofences when you are already inside the specified geofence (i.e. starting the geofence app when you are already inside the geofence).
So I changed my code in the onConnected method to add the geofences only if they are not added previously. (Implemented a check using Shared preferences)
public void onConnected(Bundle bundle) {
Log.i(TAG, "Connected to GoogleApiClient");
SharedPreferences sharedPrefs = MainActivity.this.getSharedPreferences("GEO_PREFS", Context.MODE_PRIVATE);
String geofencesExist = sharedPrefs.getString("Geofences added", null);
if (geofencesExist == null) {
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
getGeofencingRequest(),
getGeofencePendingIntent(this)
).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
SharedPreferences sharedPrefs = MainActivity.this.getSharedPreferences("GEO_PREFS", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPrefs.edit();
editor.putString("Geofences added", "1");
editor.commit();
}
}
});
}
}
2) Turns out the reasons for not receiving notifications when the app is not available were
i) Either I toggled location services in the device(on/off/battery saving/device only/high accuracy) at least once after I added the geofences or,
ii) The device was rebooted.
To overcome this, I have added a broadcast receiver to listen to device reboots and location services toggling. In the receiver, I am adding the geofences again if the device either rebooted or location services toggled.
public class BootReceiver extends BroadcastReceiver implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<Status> {
private static GoogleApiClient mGoogleApiClient;
private static List<Geofence> mGeofenceList;
private static PendingIntent mGeofencePendingIntent;
private static final String TAG = "BootReceiver";
Context contextBootReceiver;
#Override
public void onReceive(final Context context, Intent intent) {
contextBootReceiver = context;
SharedPreferences sharedPrefs;
SharedPreferences.Editor editor;
if ((intent.getAction().equals("android.location.MODE_CHANGED") && isLocationModeAvailable(contextBootReceiver)) || (intent.getAction().equals("android.location.PROVIDERS_CHANGED") && isLocationServciesAvailable(contextBootReceiver))) {
// isLocationModeAvailable for API >=19, isLocationServciesAvailable for API <19
sharedPrefs = context.getSharedPreferences("GEO_PREFS", Context.MODE_PRIVATE);
editor = sharedPrefs.edit();
editor.remove("Geofences added");
editor.commit();
if (!isGooglePlayServicesAvailable()) {
Log.i(TAG, "Google Play services unavailable.");
return;
}
mGeofencePendingIntent = null;
mGeofenceList = new ArrayList<Geofence>();
mGeofenceList.add(new Geofence.Builder()
.setRequestId("1")
.setCircularRegion(
Constants.MyAPP_LOCATION_LATITUDE,
Constants.MyAPP_LOCATION_LONGITUDE,
Constants.MyAPP_LOCATION_RADIUS
)
.setExpirationDuration(Constants.GEOFENCE_EXPIRATION_TIME)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_DWELL |
Geofence.GEOFENCE_TRANSITION_EXIT)
.setLoiteringDelay(30000)
.build());
mGoogleApiClient = new GoogleApiClient.Builder(contextBootReceiver)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
}
}
private boolean isLocationModeAvailable(Context context) {
if (Build.VERSION.SDK_INT >= 19 && getLocationMode(context) != Settings.Secure.LOCATION_MODE_OFF) {
return true;
}
else return false;
}
public boolean isLocationServciesAvailable(Context context) {
if (Build.VERSION.SDK_INT < 19) {
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
return (lm.isProviderEnabled(LocationManager.GPS_PROVIDER) || lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER));
}
else return false;
}
public int getLocationMode(Context context) {
try {
return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
}
return 0;
}
#Override
public void onConnected(Bundle bundle) {
Log.i(TAG, "Connected to GoogleApiClient");
SharedPreferences sharedPrefs = contextBootReceiver.getSharedPreferences("GEO_PREFS", Context.MODE_PRIVATE);
String geofencesExist = sharedPrefs.getString("Geofences added", null);
if (geofencesExist == null) {
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
getGeofencingRequest(),
getGeofencePendingIntent(contextBootReceiver)
).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
SharedPreferences sharedPrefs = contextBootReceiver.getSharedPreferences("GEO_PREFS", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPrefs.edit();
editor.putString("Geofences added", "1");
editor.commit();
}
}
});
}
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
if (connectionResult.hasResolution()) {
try {
connectionResult.startResolutionForResult((android.app.Activity) contextBootReceiver,
Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST);
} catch (IntentSender.SendIntentException e) {
Log.i(TAG, "Exception while resolving connection error.", e);
}
} else {
int errorCode = connectionResult.getErrorCode();
Log.i(TAG, "Connection to Google Play services failed with error code " + errorCode);
}
}
#Override
public void onResult(Status status) {
}
private boolean isGooglePlayServicesAvailable() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(contextBootReceiver);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, (android.app.Activity) contextBootReceiver,
Constants.PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
Log.i(TAG, "This device is not supported.");
}
return false;
}
return true;
}
static GeofencingRequest getGeofencingRequest() {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_DWELL);
builder.addGeofences(mGeofenceList);
return builder.build();
}
static PendingIntent getGeofencePendingIntent(Context context) {
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(context, GeofenceTransitionsIntentService.class);
return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
Android Manifest
.
.
.
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.location.MODE_CHANGED" />
<action android:name="android.location.PROVIDERS_CHANGED" />
</intent-filter>
</receiver>
.
.
Does anyone know of an example of using the LocationServices.GeofencingApi?
All the android geofencing examples I find are using the deprecated LocationClient class.
From what I can see, the LocationServices class is the one to use, but there doesn't seem to be any working examples on how to use it.
The closest I've found is this post highlighting location update requests
UPDATE: The closest answer I've found is this git example project - but it still uses the deprecated LocationClient to get triggered fences.
I just migrated my code to the new API. Here is a working example:
A working project on GitHub based on this answer: https://github.com/androidfu/GeofenceExample
This helper class registers the geofences using the API. I use a callback interface to communicate with the calling activity/fragment. You can build a callback that fits your needs.
public class GeofencingRegisterer implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private Context mContext;
private GoogleApiClient mGoogleApiClient;
private List<Geofence> geofencesToAdd;
private PendingIntent mGeofencePendingIntent;
private GeofencingRegistererCallbacks mCallback;
public final String TAG = this.getClass().getName();
public GeofencingRegisterer(Context context){
mContext =context;
}
public void setGeofencingCallback(GeofencingRegistererCallbacks callback){
mCallback = callback;
}
public void registerGeofences(List<Geofence> geofences){
geofencesToAdd = geofences;
mGoogleApiClient = new GoogleApiClient.Builder(mContext)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mGoogleApiClient.connect();
}
#Override
public void onConnected(Bundle bundle) {
if(mCallback != null){
mCallback.onApiClientConnected();
}
mGeofencePendingIntent = createRequestPendingIntent();
PendingResult<Status> result = LocationServices.GeofencingApi.addGeofences(mGoogleApiClient, geofencesToAdd, mGeofencePendingIntent);
result.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
// Successfully registered
if(mCallback != null){
mCallback.onGeofencesRegisteredSuccessful();
}
} else if (status.hasResolution()) {
// Google provides a way to fix the issue
/*
status.startResolutionForResult(
mContext, // your current activity used to receive the result
RESULT_CODE); // the result code you'll look for in your
// onActivityResult method to retry registering
*/
} else {
// No recovery. Weep softly or inform the user.
Log.e(TAG, "Registering failed: " + status.getStatusMessage());
}
}
});
}
#Override
public void onConnectionSuspended(int i) {
if(mCallback != null){
mCallback.onApiClientSuspended();
}
Log.e(TAG, "onConnectionSuspended: " + i);
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
if(mCallback != null){
mCallback.onApiClientConnectionFailed(connectionResult);
}
Log.e(TAG, "onConnectionFailed: " + connectionResult.getErrorCode());
}
/**
* Returns the current PendingIntent to the caller.
*
* #return The PendingIntent used to create the current set of geofences
*/
public PendingIntent getRequestPendingIntent() {
return createRequestPendingIntent();
}
/**
* Get a PendingIntent to send with the request to add Geofences. Location
* Services issues the Intent inside this PendingIntent whenever a geofence
* transition occurs for the current list of geofences.
*
* #return A PendingIntent for the IntentService that handles geofence
* transitions.
*/
private PendingIntent createRequestPendingIntent() {
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
} else {
Intent intent = new Intent(mContext, GeofencingReceiver.class);
return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
}
This class is the base class for your geofence transition receiver.
public abstract class ReceiveGeofenceTransitionIntentService extends IntentService {
/**
* Sets an identifier for this class' background thread
*/
public ReceiveGeofenceTransitionIntentService() {
super("ReceiveGeofenceTransitionIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
GeofencingEvent event = GeofencingEvent.fromIntent(intent);
if(event != null){
if(event.hasError()){
onError(event.getErrorCode());
} else {
int transition = event.getGeofenceTransition();
if(transition == Geofence.GEOFENCE_TRANSITION_ENTER || transition == Geofence.GEOFENCE_TRANSITION_DWELL || transition == Geofence.GEOFENCE_TRANSITION_EXIT){
String[] geofenceIds = new String[event.getTriggeringGeofences().size()];
for (int index = 0; index < event.getTriggeringGeofences().size(); index++) {
geofenceIds[index] = event.getTriggeringGeofences().get(index).getRequestId();
}
if (transition == Geofence.GEOFENCE_TRANSITION_ENTER || transition == Geofence.GEOFENCE_TRANSITION_DWELL) {
onEnteredGeofences(geofenceIds);
} else if (transition == Geofence.GEOFENCE_TRANSITION_EXIT) {
onExitedGeofences(geofenceIds);
}
}
}
}
}
protected abstract void onEnteredGeofences(String[] geofenceIds);
protected abstract void onExitedGeofences(String[] geofenceIds);
protected abstract void onError(int errorCode);
}
This class implements the abstract class and does all the handling of geofence transitions
public class GeofencingReceiver extends ReceiveGeofenceTransitionIntentService {
#Override
protected void onEnteredGeofences(String[] geofenceIds) {
Log.d(GeofencingReceiver.class.getName(), "onEnter");
}
#Override
protected void onExitedGeofences(String[] geofenceIds) {
Log.d(GeofencingReceiver.class.getName(), "onExit");
}
#Override
protected void onError(int errorCode) {
Log.e(GeofencingReceiver.class.getName(), "Error: " + i);
}
}
And in your manifest add:
<service
android:name="**xxxxxxx**.GeofencingReceiver"
android:exported="true"
android:label="#string/app_name" >
</service>
Callback Interface
public interface GeofencingRegistererCallbacks {
public void onApiClientConnected();
public void onApiClientSuspended();
public void onApiClientConnectionFailed(ConnectionResult connectionResult);
public void onGeofencesRegisteredSuccessful();
}