Android Geofence only works with the open application

I have been doing serious research on this topic for many days ... I also saw a lot of topics ... But, unfortunately, I could not find a solution ....

I am writing an application that uses the new Google API for Geofence ... Well, I can handle the "ins" and "outs" from geofence, but only if my application is open! Even if I connect to Wi-Fi, gps and 3G, but the application does not fire any event ... Just if the application is open ...

I use the exact same GeofenceRequester class of the documentation http://developer.android.com/training/location/geofencing.html .

Even the class was the same, I will post the code here:

package br.com.marrs.imhere.geofence; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentSender.SendIntentException; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import br.com.marrs.imhere.services.ReceiveTransitionsIntentService; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesClient; import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks; import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener; import com.google.android.gms.location.Geofence; import com.google.android.gms.location.LocationClient; import com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener; import com.google.android.gms.location.LocationStatusCodes; /** * Class for connecting to Location Services and requesting geofences. * <b> * Note: Clients must ensure that Google Play services is available before requesting geofences. * </b> Use GooglePlayServicesUtil.isGooglePlayServicesAvailable() to check. * * * To use a GeofenceRequester, instantiate it and call AddGeofence(). Everything else is done * automatically. * */ public class GeofenceRequester implements OnAddGeofencesResultListener, ConnectionCallbacks, OnConnectionFailedListener { // Storage for a reference to the calling client private final Activity mActivity; // Stores the PendingIntent used to send geofence transitions back to the app private PendingIntent mGeofencePendingIntent; // Stores the current list of geofences private ArrayList<Geofence> mCurrentGeofences; // Stores the current instantiation of the location client private LocationClient mLocationClient; /* * Flag that indicates whether an add or remove request is underway. Check this * flag before attempting to start a new request. */ private boolean mInProgress; public GeofenceRequester(Activity activityContext) { // Save the context mActivity = activityContext; // Initialize the globals to null mGeofencePendingIntent = null; mLocationClient = null; mInProgress = false; } /** * Set the "in progress" flag from a caller. This allows callers to re-set a * request that failed but was later fixed. * * @param flag Turn the in progress flag on or off. */ public void setInProgressFlag(boolean flag) { // Set the "In Progress" flag. mInProgress = flag; } /** * Get the current in progress status. * * @return The current value of the in progress flag. */ public boolean getInProgressFlag() { return mInProgress; } /** * Returns the current PendingIntent to the caller. * * @return The PendingIntent used to create the current set of geofences */ public PendingIntent getRequestPendingIntent() { return createRequestPendingIntent(); } /** * Start adding geofences. Save the geofences, then start adding them by requesting a * connection * * @param geofences A List of one or more geofences to add */ public void addGeofences(List<Geofence> geofences) throws UnsupportedOperationException { /* * Save the geofences so that they can be sent to Location Services once the * connection is available. */ mCurrentGeofences = (ArrayList<Geofence>) geofences; // If a request is not already in progress if (!mInProgress) { // Toggle the flag and continue mInProgress = true; // Request a connection to Location Services requestConnection(); // If a request is in progress } else { // Throw an exception and stop the request throw new UnsupportedOperationException(); } } /** * Request a connection to Location Services. This call returns immediately, * but the request is not complete until onConnected() or onConnectionFailure() is called. */ private void requestConnection() { getLocationClient().connect(); } /** * Get the current location client, or create a new one if necessary. * * @return A LocationClient object */ private GooglePlayServicesClient getLocationClient() { if (mLocationClient == null) { mLocationClient = new LocationClient(mActivity, this, this); } return mLocationClient; } /** * Once the connection is available, send a request to add the Geofences */ private void continueAddGeofences() { // Get a PendingIntent that Location Services issues when a geofence transition occurs mGeofencePendingIntent = createRequestPendingIntent(); // Send a request to add the current geofences mLocationClient.addGeofences(mCurrentGeofences, mGeofencePendingIntent, this); } /* * Handle the result of adding the geofences */ @Override public void onAddGeofencesResult(int statusCode, String[] geofenceRequestIds) { // Create a broadcast Intent that notifies other components of success or failure Intent broadcastIntent = new Intent(); // Temp storage for messages String msg; // If adding the geocodes was successful if (LocationStatusCodes.SUCCESS == statusCode) { // Create a message containing all the geofence IDs added. msg = geofenceRequestIds.toString(); // In debug mode, log the result Log.d("DEBUG", msg); // Create an Intent to broadcast to the app broadcastIntent.setAction("br.com.marrs.imhere.ACTION_GEOFENCES_ADDED") .addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES") .putExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS", msg); // If adding the geofences failed } else { /* * Create a message containing the error code and the list * of geofence IDs you tried to add */ msg = "Erro adicionando geofence"; // Log an error Log.e("DEBUG", msg); // Create an Intent to broadcast to the app broadcastIntent.setAction("br.com.marrs.imhere.ACTION_GEOFENCE_ERROR") .addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES") .putExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS", msg); } // Broadcast whichever result occurred LocalBroadcastManager.getInstance(mActivity).sendBroadcast(broadcastIntent); // Disconnect the location client requestDisconnection(); } /** * Get a location client and disconnect from Location Services */ private void requestDisconnection() { // A request is no longer in progress mInProgress = false; getLocationClient().disconnect(); } /* * Called by Location Services once the location client is connected. * * Continue by adding the requested geofences. */ @Override public void onConnected(Bundle arg0) { // If debugging, log the connection Log.d("DEBUG", "GeofenceRequester connected"); // Continue adding the geofences continueAddGeofences(); } /* * Called by Location Services once the location client is disconnected. */ @Override public void onDisconnected() { // Turn off the request flag mInProgress = false; // In debug mode, log the disconnection Log.d("DEBUG", "GeofenceRequester disconnected"); // Destroy the current location client mLocationClient = null; } /** * 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 the PendingIntent already exists if (null != mGeofencePendingIntent) { // Return the existing intent return mGeofencePendingIntent; // If no PendingIntent exists } else { // Create an Intent pointing to the IntentService Intent intent = new Intent(mActivity, ReceiveTransitionsIntentService.class); /* * Return a PendingIntent to start the IntentService. * Always create a PendingIntent sent to Location Services * with FLAG_UPDATE_CURRENT, so that sending the PendingIntent * again updates the original. Otherwise, Location Services * can't match the PendingIntent to requests made with it. */ return PendingIntent.getService( mActivity, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } } /* * Implementation of OnConnectionFailedListener.onConnectionFailed * If a connection or disconnection request fails, report the error * connectionResult is passed in from Location Services */ @Override public void onConnectionFailed(ConnectionResult connectionResult) { // Turn off the request flag mInProgress = false; /* * Google Play services can resolve some errors it detects. * If the error has a resolution, try sending an Intent to * start a Google Play services activity that can resolve * error. */ if (connectionResult.hasResolution()) { try { // Start an Activity that tries to resolve the error connectionResult.startResolutionForResult(mActivity, 9000); /* * Thrown if Google Play services canceled the original * PendingIntent */ } catch (SendIntentException e) { // Log the error e.printStackTrace(); } /* * If no resolution is available, put the error code in * an error Intent and broadcast it back to the main Activity. * The Activity then displays an error dialog. * is out of date. */ } else { Intent errorBroadcastIntent = new Intent("br.com.marrs.imhere.ACTION_CONNECTION_ERROR"); errorBroadcastIntent.addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES") .putExtra("br.com.marrs.imhere.EXTRA_CONNECTION_ERROR_CODE", connectionResult.getErrorCode()); LocalBroadcastManager.getInstance(mActivity).sendBroadcast(errorBroadcastIntent); } } } 

And the service:

 package br.com.marrs.imhere.services; import br.com.marrs.imhere.ImHereActivity; import br.com.marrs.imhere.R; import com.google.android.gms.location.Geofence; import com.google.android.gms.location.LocationClient; import android.app.IntentService; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.support.v4.app.NotificationCompat; import android.support.v4.app.TaskStackBuilder; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.Log; import java.util.List; /** * This class receives geofence transition events from Location Services, in the * form of an Intent containing the transition type and geofence id(s) that triggered * the event. */ public class ReceiveTransitionsIntentService extends IntentService { /** * Sets an identifier for this class' background thread */ 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 protected void onHandleIntent(Intent intent) { // Create a local broadcast Intent Intent broadcastIntent = new Intent(); // Give it the category for all intents sent by the Intent Service broadcastIntent.addCategory("br.com.marrs.imhere.CATEGORY_LOCATION_SERVICES"); // First check for errors if (LocationClient.hasError(intent)) { // Get the error code int errorCode = LocationClient.getErrorCode(intent); // Log the error Log.e("DEBUG", "Erro no service LocationClient has error"); // Set the action and error message for the broadcast intent broadcastIntent.setAction("br.com.marrs.imhere.ACTION_GEOFENCES_ERROR").putExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS", "problemas"); // Broadcast the error *locally* to other components in this app LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent); // If there no error, get the transition type and create a notification } else { // Get the type of transition (entry or exit) int transition = LocationClient.getGeofenceTransition(intent); // Test that a valid transition was reported if ( (transition == Geofence.GEOFENCE_TRANSITION_ENTER) || (transition == Geofence.GEOFENCE_TRANSITION_EXIT) ) { // Post a notification List<Geofence> geofences = LocationClient.getTriggeringGeofences(intent); String[] geofenceIds = new String[geofences.size()]; for (int index = 0; index < geofences.size() ; index++) { geofenceIds[index] = geofences.get(index).getRequestId(); } String ids = TextUtils.join(",",geofenceIds); String transitionType = getTransitionString(transition); sendNotification(transitionType, ids); // Log the transition type and a message Log.d("DEBUG","Ae...n sei pq isso....mas parece que tah ok"); // An invalid transition was reported } else { // Always log as an error Log.e("DEBUG","Erro, erro, erro"); } } } /** * Posts a notification in the notification bar when a transition is detected. * If the user clicks the notification, control goes to the main Activity. * @param transitionType The type of transition that occurred. * */ private void sendNotification(String transitionType, String ids) { // Create an explicit content Intent that starts the main Activity Intent notificationIntent = new Intent(getApplicationContext(),ImHereActivity.class); // Construct a task stack TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); // Adds the main Activity to the task stack as the parent stackBuilder.addParentStack(ImHereActivity.class); // Push the content Intent onto the stack stackBuilder.addNextIntent(notificationIntent); // Get a PendingIntent containing the entire back stack PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); // Get a notification builder that compatible with platform versions >= 4 NotificationCompat.Builder builder = new NotificationCompat.Builder(this); // Set the notification contents builder.setSmallIcon(R.drawable.abs__ic_clear) .setContentTitle(ids) .setContentText(transitionType) .setContentIntent(notificationPendingIntent); // Get an instance of the Notification manager NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Issue the notification mNotificationManager.notify(0, builder.build()); } /** * Maps geofence transition types to their human-readable equivalents. * @param transitionType A transition type constant defined in Geofence * @return A String indicating the type of transition */ private String getTransitionString(int transitionType) { switch (transitionType) { case Geofence.GEOFENCE_TRANSITION_ENTER: return "Entrando"; case Geofence.GEOFENCE_TRANSITION_EXIT: return "Saindo"; default: return "Desconhecido"; } } } 

And the Broadcast receiver in Office:

 public class GeofenceSampleReceiver extends BroadcastReceiver { /* * Define the required method for broadcast receivers * This method is invoked when a broadcast Intent triggers the receiver */ @Override public void onReceive(Context context, Intent intent) { // Check the action code and determine what to do String action = intent.getAction(); // Intent contains information about errors in adding or removing geofences if (TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCE_ERROR")) { handleGeofenceError(context, intent); // Intent contains information about successful addition or removal of geofences } else if ( TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCES_ADDED") || TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCES_REMOVED")) { handleGeofenceStatus(context, intent); // Intent contains information about a geofence transition } else if (TextUtils.equals(action, "br.com.marrs.imhere.ACTION_GEOFENCE_TRANSITION")) { handleGeofenceTransition(context, intent); // The Intent contained an invalid action } else { Log.e("DEBUG", "Invalid action detail"); Toast.makeText(context, "Invalid action detail", Toast.LENGTH_LONG).show(); } } /** * If you want to display a UI message about adding or removing geofences, put it here. * * @param context A Context for this component * @param intent The received broadcast Intent */ private void handleGeofenceStatus(Context context, Intent intent) { } /** * Report geofence transitions to the UI * * @param context A Context for this component * @param intent The Intent containing the transition */ private void handleGeofenceTransition(Context context, Intent intent) { /* * If you want to change the UI when a transition occurs, put the code * here. The current design of the app uses a notification to inform the * user that a transition has occurred. */ } /** * Report addition or removal errors to the UI, using a Toast * * @param intent A broadcast Intent sent by ReceiveTransitionsIntentService */ private void handleGeofenceError(Context context, Intent intent) { String msg = intent.getStringExtra("br.com.marrs.imhere.EXTRA_GEOFENCE_STATUS"); Log.e("DEBUG", msg); Toast.makeText(context, msg, Toast.LENGTH_LONG).show(); } } 

And here is the code snippet that I use to create GEofence before submitting it to GeofenceRequester.

 int raio = Integer.parseInt(spinner.getAdapter().getItem(spinner.getSelectedItemPosition()).toString()); int transitionType = (in.isChecked())?Geofence.GEOFENCE_TRANSITION_ENTER:Geofence.GEOFENCE_TRANSITION_EXIT; Geofence geofence = new Geofence.Builder().setRequestId(nomeGeofence.getText().toString()).setTransitionTypes(transitionType).setCircularRegion(lat, lon, raio).setExpirationDuration(Geofence.NEVER_EXPIRE).build(); geofences.add(geofence); try { mGeofenceRequester.addGeofences(geofences); addCircleGeofence(raio); } catch (UnsupportedOperationException e) { Toast.makeText(getActivity(), "Já existe uma requisição de add em andamento",Toast.LENGTH_LONG).show(); } 

Any help would be great! Thanks!

+5
source share
2 answers

I had the exact same problem. Here's what I answered there: so after playing around a bit with this, it seems that ReceiveTransitionsIntentService (as defined in the code example) will stop receiving notifications when the application does not exist. I think this is a big problem with the sample code ... It seems like this will suit people like me.

So, I used the broadcast receiver, and so far it seems to work from my tests.

Add this to the manifest:

 <receiver android:name="com.aol.android.geofence.GeofenceReceiver" android:exported="false"> <intent-filter > <action android:name="com.aol.android.geofence.ACTION_RECEIVE_GEOFENCE"/> </intent-filter> </receiver> 

Then in the GeofenceRequester class you need to change the createRequestPendingIntent method so that it is passed to your BroadcastReceiver instead of ReceiveTransitionsIntentService. MAKE SURE AND SPECIFY the change to .getBroadcast instead of getService. It made me hang for a while.

 private PendingIntent createRequestPendingIntent() { // If the PendingIntent already exists if (null != mGeofencePendingIntent) { // Return the existing intent return mGeofencePendingIntent; // If no PendingIntent exists } else { // Create an Intent pointing to the IntentService Intent intent = new Intent("com.aol.android.geofence.ACTION_RECEIVE_GEOFENCE"); //MAKE SURE YOU CHANGE THIS TO getBroadcast if you are coming from the sample code. return PendingIntent.getBroadcast( context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } } 

Then I added the GeofenceReceiver class, which looks something like this:

 public class GeofenceReceiver extends BroadcastReceiver { Context context; Intent broadcastIntent = new Intent(); @Override public void onReceive(Context context, Intent intent) { this.context = context; broadcastIntent.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES); if (LocationClient.hasError(intent)) { handleError(intent); } else { handleEnterExit(intent); } } private void handleError(Intent intent){ // Get the error code int errorCode = LocationClient.getErrorCode(intent); // Get the error message String errorMessage = LocationServiceErrorMessages.getErrorString( context, errorCode); // Log the error Log.e(GeofenceUtils.APPTAG, context.getString(R.string.geofence_transition_error_detail, errorMessage)); // Set the action and error message for the broadcast intent broadcastIntent .setAction(GeofenceUtils.ACTION_GEOFENCE_ERROR) .putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, errorMessage); // Broadcast the error *locally* to other components in this app LocalBroadcastManager.getInstance(context).sendBroadcast( broadcastIntent); } private void handleEnterExit(Intent intent) { // Get the type of transition (entry or exit) int transition = LocationClient.getGeofenceTransition(intent); // Test that a valid transition was reported if ((transition == Geofence.GEOFENCE_TRANSITION_ENTER) || (transition == Geofence.GEOFENCE_TRANSITION_EXIT)) { // Post a notification List<Geofence> geofences = LocationClient .getTriggeringGeofences(intent); String[] geofenceIds = new String[geofences.size()]; String ids = TextUtils.join(GeofenceUtils.GEOFENCE_ID_DELIMITER, geofenceIds); String transitionType = GeofenceUtils .getTransitionString(transition); for (int index = 0; index < geofences.size(); index++) { Geofence geofence = geofences.get(index); ...do something with the geofence entry or exit. I'm saving them to a local sqlite db } // Create an Intent to broadcast to the app broadcastIntent .setAction(GeofenceUtils.ACTION_GEOFENCE_TRANSITION) .addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES) .putExtra(GeofenceUtils.EXTRA_GEOFENCE_ID, geofenceIds) .putExtra(GeofenceUtils.EXTRA_GEOFENCE_TRANSITION_TYPE, transitionType); LocalBroadcastManager.getInstance(MyApplication.getContext()) .sendBroadcast(broadcastIntent); // Log the transition type and a message Log.d(GeofenceUtils.APPTAG, transitionType + ": " + ids); Log.d(GeofenceUtils.APPTAG, context.getString(R.string.geofence_transition_notification_text)); // In debug mode, log the result Log.d(GeofenceUtils.APPTAG, "transition"); // An invalid transition was reported } else { // Always log as an error Log.e(GeofenceUtils.APPTAG, context.getString(R.string.geofence_transition_invalid_type, transition)); } } /** * Posts a notification in the notification bar when a transition is * detected. If the user clicks the notification, control goes to the main * Activity. * * @param transitionType * The type of transition that occurred. * */ private void sendNotification(String transitionType, String locationName) { // Create an explicit content Intent that starts the main Activity Intent notificationIntent = new Intent(context, MainActivity.class); // Construct a task stack TaskStackBuilder stackBuilder = TaskStackBuilder.create(context); // Adds the main Activity to the task stack as the parent stackBuilder.addParentStack(MainActivity.class); // Push the content Intent onto the stack stackBuilder.addNextIntent(notificationIntent); // Get a PendingIntent containing the entire back stack PendingIntent notificationPendingIntent = stackBuilder .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); // Get a notification builder that compatible with platform versions // >= 4 NotificationCompat.Builder builder = new NotificationCompat.Builder( context); // Set the notification contents builder.setSmallIcon(R.drawable.ic_notification) .setContentTitle(transitionType + ": " + locationName) .setContentText( context.getString(R.string.geofence_transition_notification_text)) .setContentIntent(notificationPendingIntent); // Get an instance of the Notification manager NotificationManager mNotificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); // Issue the notification mNotificationManager.notify(0, builder.build()); } } 

Hope this helps someone else.

+15
source

I had a similar problem, and by trying in various ways, I could finally fix this problem. Unfortunately, the Android documentation does not mention these issues.
Geofences android are deleted every time you reboot the device or every time you switch the location mode. So, add a broadcast receiver to listen for device reboots and location mode changes, and again add geo objects to the receiver.

I posted a detailed explanation here

+2
source

Source: https://habr.com/ru/post/985251/


All Articles