Geofence Notification not triggered when app in background
Asked Answered
H

2

9

I have already gone through many SO posts , but nothing has worked yet for me. I am trying to trigger notification when device enters a geofence . But it does not trigger until app is opened. How would it be possible to trigger notification when app is in backgroud ?

Geofencing:

public class Geofencing implements ResultCallback {

    // Constants
    public static final String TAG = Geofencing.class.getSimpleName();
    private static final float GEOFENCE_RADIUS = 50; // 50 meters
    private static final long GEOFENCE_TIMEOUT = 24 * 60 * 60 * 1000; // 24 hours

    private List<Geofence> mGeofenceList;
    private PendingIntent mGeofencePendingIntent;
    private GoogleApiClient mGoogleApiClient;
    private Context mContext;

    public Geofencing(Context context, GoogleApiClient client) {
        mContext = context;
        mGoogleApiClient = client;
        mGeofencePendingIntent = null;
        mGeofenceList = new ArrayList<>();
    }

    /***
     * Registers the list of Geofences specified in mGeofenceList with Google Place Services
     * Uses {@code #mGoogleApiClient} to connect to Google Place Services
     * Uses {@link #getGeofencingRequest} to get the list of Geofences to be registered
     * Uses {@link #getGeofencePendingIntent} to get the pending intent to launch the IntentService
     * when the Geofence is triggered
     * Triggers {@link #onResult} when the geofences have been registered successfully
     */
    public void registerAllGeofences() {
        // Check that the API client is connected and that the list has Geofences in it
        if (mGoogleApiClient == null || !mGoogleApiClient.isConnected() ||
                mGeofenceList == null || mGeofenceList.size() == 0) {
            return;
        }
        try {
            LocationServices.GeofencingApi.addGeofences(
                    mGoogleApiClient,
                    getGeofencingRequest(),
                    getGeofencePendingIntent()
            ).setResultCallback(this);
        } catch (SecurityException securityException) {
            // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
            Log.e(TAG, securityException.getMessage());
        }
    }

    /***
     * Unregisters all the Geofences created by this app from Google Place Services
     * Uses {@code #mGoogleApiClient} to connect to Google Place Services
     * Uses {@link #getGeofencePendingIntent} to get the pending intent passed when
     * registering the Geofences in the first place
     * Triggers {@link #onResult} when the geofences have been unregistered successfully
     */
    public void unRegisterAllGeofences() {
        if (mGoogleApiClient == null || !mGoogleApiClient.isConnected()) {
            return;
        }
        try {
            LocationServices.GeofencingApi.removeGeofences(
                    mGoogleApiClient,
                    // This is the same pending intent that was used in registerGeofences
                    getGeofencePendingIntent()
            ).setResultCallback(this);
        } catch (SecurityException securityException) {
            // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
            Log.e(TAG, securityException.getMessage());
        }
    }


    /***
     * Updates the local ArrayList of Geofences using data from the passed in list
     * Uses the Place ID defined by the API as the Geofence object Id
     *
     * @param places the PlaceBuffer result of the getPlaceById call
     */
    public void updateGeofencesList(PlaceBuffer places) {
        mGeofenceList = new ArrayList<>();
        if (places == null || places.getCount() == 0) return;
        for (Place place : places) {
            // Read the place information from the DB cursor
            String placeUID = place.getId();
            double placeLat = place.getLatLng().latitude;
            double placeLng = place.getLatLng().longitude;
            // Build a Geofence object
            Geofence geofence = new Geofence.Builder()
                    .setRequestId(placeUID)
                    .setExpirationDuration(GEOFENCE_TIMEOUT)
                    .setCircularRegion(placeLat, placeLng, GEOFENCE_RADIUS)
                    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)
                    .build();
            // Add it to the list
            mGeofenceList.add(geofence);
        }
    }

    /***
     * Creates a GeofencingRequest object using the mGeofenceList ArrayList of Geofences
     * Used by {@code #registerGeofences}
     *
     * @return the GeofencingRequest object
     */
    private GeofencingRequest getGeofencingRequest() {
        GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
        builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
        builder.addGeofences(mGeofenceList);
        return builder.build();
    }

    /***
     * Creates a PendingIntent object using the GeofenceTransitionsIntentService class
     * Used by {@code #registerGeofences}
     *
     * @return the PendingIntent object
     */
    private PendingIntent getGeofencePendingIntent() {
        // Reuse the PendingIntent if we already have it.
        if (mGeofencePendingIntent != null) {
            return mGeofencePendingIntent;
        }
        //Intent intent = new Intent(mContext, GeofenceBroadcastReceiver.class);
        Intent intent = new Intent("com.aol.android.geofence.ACTION_RECEIVE_GEOFENCE");
        mGeofencePendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.
                FLAG_UPDATE_CURRENT);
        return mGeofencePendingIntent;
    }

    @Override
    public void onResult(@NonNull Result result) {
        Log.e(TAG, String.format("Error adding/removing geofence : %s",
                result.getStatus().toString()));
    }

}

GeofenceBroadcastReceiver:

public class GeofenceBroadcastReceiver extends BroadcastReceiver {

    public static final String TAG = GeofenceBroadcastReceiver.class.getSimpleName();

    /***
     * Handles the Broadcast message sent when the Geofence Transition is triggered
     * Careful here though, this is running on the main thread so make sure you start an AsyncTask for
     * anything that takes longer than say 10 second to run
     *
     * @param context
     * @param intent
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        // Get the Geofence Event from the Intent sent through

        Log.d("onRecccc","trt");

        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        if (geofencingEvent.hasError()) {
            Log.e(TAG, String.format("Error code : %d", geofencingEvent.getErrorCode()));
            return;
        }

        // Get the transition type.
        int geofenceTransition = geofencingEvent.getGeofenceTransition();
        // Check which transition type has triggered this event

        // Send the notification
        sendNotification(context, geofenceTransition);
    }


    /**
     * Posts a notification in the notification bar when a transition is detected
     * Uses different icon drawables for different transition types
     * If the user clicks the notification, control goes to the MainActivity
     *
     * @param context        The calling context for building a task stack
     * @param transitionType The geofence transition type, can be Geofence.GEOFENCE_TRANSITION_ENTER
     *                       or Geofence.GEOFENCE_TRANSITION_EXIT
     */
    private void sendNotification(Context context, int transitionType) {
        // 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);

        // Add 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
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

        // Check the transition type to display the relevant icon image
        if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER) {
            builder.setSmallIcon(R.drawable.ic_near_me_black_24dp)
                    .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
                            R.drawable.ic_near_me_black_24dp))
                    .setContentTitle("You have a task nearby")
                    .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
                    //Vibration
                    .setVibrate(new long[]{300,300})
                    .setLights(Color.RED, 3000, 3000);
                    //LED


        } else if (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
            builder.setSmallIcon(R.drawable.ic_near_me_black_24dp)
                    .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
                            R.drawable.ic_near_me_black_24dp))
                    .setContentTitle(context.getString(R.string.back_to_normal));
        }

        // Continue building the notification
        builder.setContentText(context.getString(R.string.touch_to_relaunch));
        builder.setContentIntent(notificationPendingIntent);

        // Dismiss notification once the user touches it.
        builder.setAutoCancel(true);

        // Get an instance of the Notification manager
        NotificationManager mNotificationManager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        // Issue the notification
        mNotificationManager.notify(0, builder.build());
    }

}

EDIT :

 @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //Create geofences from SharedPreferences/network responses
        //Connect to location services


        mClient = new GoogleApiClient.Builder(this)

                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .addApi(Places.GEO_DATA_API)
                .addApi(Places.PLACE_DETECTION_API)
                .build();

        mGeofencing = new Geofencing(this, mClient);
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        if (geofencingEvent.hasError()) {
            Log.e("dsadsa", String.format("Error code : %d", geofencingEvent.getErrorCode()));
            return;
        }
    }


    public void onConnected(Bundle bundle) {
        //Add geofences
        mGeofencing.registerAllGeofences();

    }

I have done this so far , and still no luck ..

Hecto answered 31/7, 2017 at 18:9 Comment(2)
read comments #45218570 might be helpfulKalbli
I updated my answer...Mccraw
M
0

I have the same problem when the application is on the background (user clicked on Home button, or multiple back until he see the Home Screen).

I tried to solve it with registering a BroadcastReceiver in the Manifest instead of IntentService.. It doesn't help so much as I've got the same results..

Then, I tried this: Open the application, added a geofence and went to the Home Screen. As you probably understand the geofence didn't trigger.. But when I clicked on Google Maps instead my application.. It was triggered!!

So it seems that is working on background if there are any apps that request for location updates (like google maps).

So I tried this approach: I created a sticky service for requesting location updates using the LocationServices.FusedLocationApi.. this service contains GoogleApiClient and implements GoogleApiClient.ConnectionCallbacks and GoogleApiClient.OnConnectionFailedListener

But guess what? It still doesn't work on background :(

Update: After I tried so many times to make it work.. it finally worked! I have an Android emulator with Google Play services (version 11.3.02) and Android 7.0 If you want a good explanation of how to work with Geofence and how to check it with the emulator take a look at this link

Now, I've tried geofencing with this emulator when the application is on the foreground and then in the background and it worked!

When I said that it didn't work for me on the background, the Android version on that emulator was Android 8. So I guess I need to find a solution for Android 8 -> a good start is this this documentation link where they explain how they handle now with background and foreground applications.

Mccraw answered 1/8, 2017 at 15:18 Comment(11)
mine isn't even triggering when maps are opened :') , please do let me know and post your code if you figure it out..Hecto
@user3820753 Hi, I'm using Android studio emulator with Google Play Services installed on it. What I did is first to set the emulator location lat and long to 0 and clicked on Send. Then open my app in order to add a Geofence and pressed the Home button. Then I set the emulator lat and long to the geofence lat and long value and clicked on Send. And finally open the Google Maps application..Mccraw
@Mccraw yeah, it works like a charm on Android 7 although I don't understand why we have to request location updates...geofences should be triggered by Play Services....that's weird. However on Android 8 it would be hard to make it works due to background limitations. Let's see what we can achieve.Schooner
@GiulioBider Yeah you're right, it is weird, and I also thought like you and at first didn't asked for location updates, but nothing happened. I had the Geofencing example from google and when I changed the emulator's location to the Geofence Lat and Long nothing happened until I opened Google Maps! So it seems that the Geofencing works correctly when there is an application on background that requests for location updates (Waze or Google Maps). I must say that I it both on Android 7 & 8. So I think the best solution is that your apps should request for location updates.Mccraw
@GiulioBider as for Android 8, I know.. it's driving me crazy :) They suggest to use Foreground Service so the application will be keep on foreground and then probably the geofence will be triggered also when the application is on background. Another solution that I saw (and trying it first) is to register a BroadcastReceiver and instead of doing PendingIntent.getService, do PendingIntent.getBroadcast.. also there must be a logic that checks if the Android Version is greater or equals to Android 8, then use the PendingIntent.getBroadcast. else use PendingIntent.getServiceMccraw
@GiulioBider see this link codelabs.developers.google.com/codelabs/…Mccraw
@Mccraw I've already tried the way of Broadcast instead Service and it seems work more or less. Foreground service is not a valid option for me because I have to show a permanent notification. Now the problem is: how much battery is drained by a constant location updates in background to monitor geofences? :DSchooner
@GiulioBider if your battery settings for your app is set to "Optimize" I don't think it would drain the battery like crazy :) They say that the updates will occur few times each hour.Mccraw
In my case I think I'll use the foreground service. So when the user ask to monitor geofence, I'll start a service that ask for location updates (foreground service) and when it arrived to the location I'll notify the service to stop foreground (something like that). Also, notice that they changed the way notifications work on Android 8, now the notification have channels.Mccraw
@Mccraw about battery drainage I was referring to Android 7 and below, but yes with those settings it should work fine, I'll do some tests. Thanks for your collaboration ;)Schooner
I can confirm that with BroadcastReceiver instead of Service the fences are triggered correctlyScottiescottish
F
-1

The code that you posted is about registering geofences when app is running + handling geofences events. Additionally, according to the documentation, there are five events where you should re-register your geofences:

  1. The device is rebooted. The app should listen for the device's boot complete action, and then re- register the geofences required.
  2. The app is uninstalled and re-installed.
  3. The app's data is cleared.
  4. Google Play services data is cleared.
  5. The app has received a GEOFENCE_NOT_AVAILABLE alert. This typically happens after NLP (Android's Network Location Provider) is disabled.

Lets figure them out one by one:

Regarding 2 & 3 there's nothing to do, and if the geofences are assigned to some sort of authenticated activity in your app then you don't really want them at all.

Regarding 4, it's pretty much like 2 & 3, I haven't tried to dive deep into this but I don't think there's a way to listen to this event.

1 can be solved pretty easily by registering a BroadcastReceiver:

public class BootBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent startServiceIntent = new Intent(context, AddingGeofencesService.class);
        context.startService(startServiceIntent);
    }
}

Note AddingGeofencesService, a service you should create to add geofences once an intent is received in the BootBroadcastReceiver. Something like this:

public class AddingGeofencesService extends IntentService implements GoogleApiClient.ConnectionCallbacks {

    public AddingGeofencesService() {
        super("AddingGeofencesService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
            //Create geofences from SharedPreferences/network responses
            //Connect to location services
        }
    }

    public void onConnected(Bundle bundle) {
        //Add geofences
    }
    ...
}

And let's not forget the manifest code:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<service android:name=".AddingGeofencesService"/>

<receiver android:name=".BootBroadcastReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

5 is referring mostly to changes in location providers. The solution for this case is a BroadcastReceiver as well.

public class LocationProviderChangedBroadcastReceiver extends BroadcastReceiver {
    boolean isGpsEnabled;
    boolean isNetworkEnabled;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().matches("android.location.PROVIDERS_CHANGED"))
        {
            LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
            isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            if (isGpsEnabled || isNetworkEnabled) {
                Intent startServiceIntent = new Intent(context, AddingGeofencesService.class);
                context.startService(startServiceIntent);
            }
        }
    }
}

Manifest:

<receiver
    android:name=".LocationProviderChangedBroadcastReceiver"
    android:exported="false" >
    <intent-filter>
        <action android:name="android.location.PROVIDERS_CHANGED" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</receiver>

EDIT:

I'm supplying here the code I use to manage geofences. It comes in addition to the answer above.

I excluded subclasses of LocationServicesManager that are not relevant to the answer.

/*
 * This class does not handle permission checks/missing permissions. The context that's containing
 * this class is responsible of that.
 */
public class LocationServicesManager implements
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    private static final String TAG = "YOURTAG";

    private GoogleApiClient mGoogleApiClient;
    private Context context;

    public GeofencesManager geofencesManager;

    private OnGoogleServicesConnectedListener onGoogleServicesConnectedListener;

    public LocationServicesManager(Context context,
                                   OnGoogleServicesConnectedListener onGoogleServicesConnectedListener) {
        this.context = context;
        this.onGoogleServicesConnectedListener = onGoogleServicesConnectedListener;
        buildGoogleApiClient(context);
    }

    public void GeofencesManager() {
        geofencesManager = new GeofencesManager();
    }

    //region Definition, handling connection
    private synchronized void buildGoogleApiClient(Context context) {
        mGoogleApiClient = new GoogleApiClient.Builder(context)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    public void connect() {
        mGoogleApiClient.connect();
    }

    public void disconnect() {
        if (mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }

    public boolean isConnected() {
        return mGoogleApiClient.isConnected();
    }

    @SuppressWarnings({"MissingPermission"})
    @Override
    public void onConnected(Bundle connectionHint) {
        onGoogleServicesConnectedListener.onGoogleServicesConnected();
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult result) {
        Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
    }


    @Override
    public void onConnectionSuspended(int cause) {
        // Trying to re-establish the connection.
        Log.i(TAG, "Connection suspended");
        mGoogleApiClient.connect();
    }
    //endregion

    public class GeofencesManager implements ResultCallback<Status> {

        private ArrayList<Geofence> mGeofenceList = new ArrayList<>();

        private PendingIntent mGeofencePendingIntent = null;

        private GeofencesManager() {

        }

        public void addGeofenceToList(String key, long expirationDuration, Location location, int radius) {
            addGeofenceToList(key, expirationDuration, new LatLng(location.getLatitude(), location.getLongitude()), radius);
        }

        public void addGeofenceToList(String key, long expirationDuration, LatLng location, int radius) {
            if (location != null) {
                mGeofenceList.add(new Geofence.Builder()
                        .setRequestId(key)
                        .setCircularRegion(location.latitude, location.longitude, radius)
                        .setExpirationDuration(expirationDuration)
                        .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_DWELL
                                | Geofence.GEOFENCE_TRANSITION_ENTER
                                | Geofence.GEOFENCE_TRANSITION_EXIT)
                        .setLoiteringDelay(1000 * 30)
                        .build());
            }
        }

        /**
         * Runs when the result of calling addGeofences() and removeGeofences() becomes available.
         * Either method can complete successfully or with an error.
         */
        public void onResult(@NonNull Status status) {
            if (status.isSuccess()) {
                Log.i(TAG, "onResult: " + status.toString());
            } else {
                Log.e(TAG, getGeofenceErrorString(status.getStatusCode()));
            }
        }

        /**
         * Gets a PendingIntent to send with the request to add or remove 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 getGeofencePendingIntent() {
            if (mGeofencePendingIntent != null) {
                return mGeofencePendingIntent;
            }

            Intent intent = new Intent(context, GeofenceTransitionsIntentService.class);
            // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
            return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        }

        /**
         * Builds and returns a GeofencingRequest. Specifies the list of geofences to be monitored.
         * Also specifies how the geofence notifications are initially triggered.
         */
        @NonNull
        private GeofencingRequest getGeofencingRequest() {
            GeofencingRequest.Builder builder = new GeofencingRequest.Builder();

            // The INITIAL_TRIGGER_ENTER flag indicates that geofencing service should trigger a
            // GEOFENCE_TRANSITION_ENTER notification when the geofence is added and if the device
            // is already inside that geofence.
            builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);

            // Add the geofences to be monitored by geofencing service.
            // Empty mGeofenceList leads to crash
            builder.addGeofences(mGeofenceList);

            return builder.build();
        }

        public void addGeofences() {
            if (mGeofenceList.size() > 0) {
                try {
                    LocationServices.GeofencingApi.addGeofences(
                            mGoogleApiClient,
                            getGeofencingRequest(),
                            getGeofencePendingIntent()
                    ).setResultCallback(this);
                } catch (SecurityException securityException) {
                    Crashlytics.logException(securityException);
                    Log.e(TAG, "Missing permission ACCESS_FINE_LOCATION", securityException);
                }
            }
        }

        public void removeGeofences() {
            if (mGeofenceList.size() > 0) {
                LocationServices.GeofencingApi.removeGeofences(
                        mGoogleApiClient,
                        getGeofencePendingIntent()
                ).setResultCallback(this); // Result processed in onResult().
            }
        }
    }

    public static String getGeofenceErrorString(int errorCode) {
        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";
        }
    }
}

The aforementioned interface:

public interface OnGoogleServicesConnectedListener {
    void onGoogleServicesConnected();
}

Class GeofenceTransitionsIntentService:

/**
 * Listener for geofence transition changes.
 *
 * Receives geofence transition events from Location Services in the form of an Intent containing
 * the transition type and geofence id(s) that triggered the transition. 
 */
public class GeofenceTransitionsIntentService extends IntentService {

    private static final String TAG = "YOURTAG";

    public GeofenceTransitionsIntentService() {
        super(TAG);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    /**
     * Handles incoming intents.
     * @param intent sent by Location Services. This Intent is provided to Location
     *               Services (inside a PendingIntent) when addGeofences() is called.
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        //Do stuff with the geofencing events
    }
}

In manifest:

<service android:name=".GeofenceTransitionsIntentService"/>

And finally, summed-up AddingGeofencesService:

public class AddingGeofencesService extends IntentService implements OnGoogleServicesConnectedListener {

    private static final String TAG = "YOURTAG";

    LocationServicesManager locationServicesManager;

    public AddingGeofencesService() {
        super(TAG);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
            locationServicesManager = new LocationServicesManager(this, this);
            locationServicesManager.GeofencesManager();

            //Fetch your geofences from somewhere
            List<YourGeofenceObject> yourGeofences = ...

            for (YourGeofenceObject geo : yourGeofences) {
                locationServicesManager.geofencesManager.addGeofenceToList(geo.getKey(),
                        geo.getExpirationDuration(), geo.getLocation(), geo.getRadius());
            }

            locationServicesManager.connect();
        }
    }

    @Override
    public void onGoogleServicesConnected() {
        locationServicesManager.geofencesManager.addGeofences();
    }
}

Note that you should add geofences when app is running in the same way that you add them in the AddingGeofencesService.

Fenestra answered 31/7, 2017 at 18:36 Comment(14)
hey , can u please describe more what to call inside those intent service methods , i am a bit confused , do i have to send notification from this service only ? can u please update , thanksHecto
@user3820753 This service isn't supposed to handle geofences events, so GeofencingEvent.fromIntent(intent) shouldn't be there. Instead you should register your geofences by fetching that from somewhere, connecting to service with mGoogleApiClient.connect() and finally call registerAllGeofences() inside onConnected().Fenestra
@NeriaNachum thanks for your explaination. but does it work for you when the application is on background? i.e the user clicked on home button.. ?Mccraw
@Mccraw Yes it does. One point to mention is that for certain vendors (such as Xiaomi), on Android 6.0+ you must enable 'Autostart' permission for your app on the device for background events to start.Fenestra
@NeriaNachum Thanks !Mccraw
@Mccraw Make sure that you did all of the following: 1. Adding geofences when the app is starting. 2. Adding the same geofences in an IntentService triggered by the BroadcastReceivers mentioned in my answer. 3. For 6.0+ devices, enable 'Autostart. The service implementation is up to the way you prefer to manage location services as long as you call LocationServices.GeofencingApi.addGeofences` in it.Fenestra
sorry Neria , not happening , is it possible if you could show working code if you have any or if you know any reference ?Hecto
@user3820753 I posted additional code that should help you figure this out. It's pretty much all of the relevant code.Fenestra
Any reason for the downvotes? The answer and the supplied code are thorough and it's working in my app as expected.Fenestra
developer.android.com/about/versions/oreo/… writing an geofence background app is not possible anymore in my opinion.Gallant
@NeriaNachum , i've tried to build a sample (github.com/NiasSt90/Geofence4Fhem) to show the problems with geofences on android 8.0. The geofences are working only with a ForegroundService and someone requests regularly the location (as in my sample app).Gallant
@MarkusSchulz This sample worked well pre-Oreo. With the new restrictions on background services in Oreo, there's a good chance that it became obsolete. Anyway, Google changed their API so this answer isn't relevant anymore.Fenestra
@NeriaNachum: what do you mean with "changed their API"? The new LocationServices.getGeofencingClient(..)? this new API don't change the behavior of the geofences (i've tried in my example app). I would like to have again a working geofance app (trigger a GET call on enter/leave some geofances). But i see no way to get this or?Gallant
@MarkusSchulz Yes, this is what I referred to. I'm pretty sure that on Oreo+ you cannot get real time geofece updates but I haven't investigated the issue enough to be able to help you, sorry.Fenestra

© 2022 - 2024 — McMap. All rights reserved.