How to do geofencing when app is unresponsive
Asked Answered
O

1

6

The new Geofencing API from Google is not triggering any of the events when the app is dead. I tried using PendingIntent.getBroadcast() and PendingIntent.getService() but only being able to get the transition events when the app is opened.

I followed this tutorial from code labs but adapted the code to use the new GeofencingClient

UPDATE

This is how I create the pending intent:

private PendingIntent getGeofencePendingIntent() {
    if (mGeofencePendingIntent != null) {
        Log.d(TAG, "Pending intent is already there");
        return mGeofencePendingIntent;
    }
    Log.d(TAG, "Creating a new pending intent");

    // In case I'm using Service, the second parameter will be GeoIntentService.class
    Intent mIntent = new Intent(mContext, GeoReceiver.class);

    // In case I'm using Service, the method will be getService(...)
    mGeofencePendingIntent = PendingIntent.getBroadcast(mContext, 0, mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        return mGeofencePendingIntent;
 }

The code inside my GeoReceiver's onReceive method. Somehow the code is similar when using Services inside onHandleIntent method

@Override
public void onReceive(Context context, Intent intent) {
    Log.d(TAG, "onReceive");
    if (intent == null) {
        Log.e(TAG, "Intent is null");
        return;
    }
    final String action = intent.getAction();
    if (ACTION_ADD_GEOFENCE.equals(action)) {
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        if (geofencingEvent.hasError()) {
            String errMsg = GeofenceExceptionMessages.getErrorString(context, geofencingEvent.getErrorCode());
            Log.e(TAG, "onReceive Error: " + errMsg);
            return;
        }
        int geofenceTransition = geofencingEvent.getGeofenceTransition();
        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                    geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

            List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();

            NotificationHelper helper = new NotificationHelper();

            String geofenceTransitionDetails = helper.getGeofenceTransitionDetails(
                    context,
                    geofenceTransition,
                    triggeringGeofences
            );

            helper.sendNotificaiton(context, geofenceTransitionDetails);
            Log.i(TAG, "onReceive: " + geofenceTransitionDetails);
        }
    } else {
        Log.d(TAG, "Different Action: " + action);
    }
}
Ophicleide answered 4/12, 2017 at 12:8 Comment(13)
Can you post the code setting your IntentService and the IntentService itself, please?Shirleeshirleen
@ThomasStevens please check updatesOphicleide
And the GeoReceiver class extends IntentService?Shirleeshirleen
@ThomasStevens no, my GeoReceiver extends BroadcastReceiver and GeoIntentService extends IntentServiceOphicleide
IntentService is a class specifically to run short tasks while your app is in the background (or foreground), a broadcast receiver won't receiver anything while your app is in the background, an IntentService will.Shirleeshirleen
@ThomasStevens a lot of tutorials suggested using receivers. See this codelab codelabs.developers.google.com/codelabs/… However, as mentioned I tried to use IntentService as well but nothing happend.Ophicleide
I would check 2 things to start with, the first is that your IntentService is registered in the manifest, and the second is that you are adding the geoFences correctly. BroadcastReceivers below api 26 can start the app, but for compatibility across all devices IntentService is more reliable. broadcastsShirleeshirleen
@ThomasStevens Yes the intent service is registered in the manifest. And when adding geofences I go in the onSuccess callback. Also it is showing notifications when the activity is running. Could it be that I'm using the wrong context when calling LocationServices.getGeofencingClientOphicleide
mGeofencingClient = LocationServices.getGeofencingClient(getActivity()); Is what I have in the onResume() of my location fragmentShirleeshirleen
I'm using the context constructor like this: mGeofencingClient = LocationServices.getGeofencingClient(App.getInstance()). Where App is my application class.Ophicleide
If you are constructing this in an activity or fragment it is best to use the provided context, rather than instantiate a new AppShirleeshirleen
is this getGeofencePendingIntent() running in service ?Root
@Root no it is not. I put all the geofencing code in one separate package and I'm calling a constructor and a method e.g. "monitorGeofence(geofence)" from my Activity.Ophicleide
S
0

Geofencing is not working because some devices only allow background services for some whitelist app by default. If your app also has to work like that means you have to enable AutoStart from settings, below code will help you to make user to enable autostart for your app.If AutoStart is enabled, your service will work well in background.

For the better performance of the Phone, some companies will stop all the background services of the app("Some whitelisted App's will only have the permission to do services in background, eg: WhatsApp,Google Apps and well recognized apps.").So If our app also has to work like that means,we have to enable Autostart Services.

AutoStart:

When an Android system boots, it sends out a boot complete event. Android applications can listen and capture this event to take specific actions, such as automatically starting an activity or service.

As of now,there is no way to determine whether AutoStart is enabled or not. So you can redirect them to settings and tell user to enable it.I'am providing codes for redirecting for most common phone's companies which will kill background service.(Show it to user only for once, manage it using SharedPreference, as i told earlier there is no way to determine whether it is enabled or disabled.)

To Achieve this,

Declare the permission in AndroidManifest.xml. Add the android.permission.RECEIVE_BOOT_COMPLETED permission to your application's manifest file just before the application declaration node:

This is the function code which has to be called.

private void enableAutoStart() {
    if (Build.BRAND.equalsIgnoreCase("xiaomi")) {
      new MaterialDialog.Builder(MainActivity.this).title("Enable AutoStart")
        .content(
          "Please allow AppName to always run in the background,else our services can't be accessed.")
        .theme(Theme.LIGHT)
        .positiveText("ALLOW")
        .onPositive(new MaterialDialog.SingleButtonCallback() {
          @Override
          public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {

            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.miui.securitycenter",
              "com.miui.permcenter.autostart.AutoStartManagementActivity"));
            startActivity(intent);
          }
        })
        .show();
    } else if (Build.BRAND.equalsIgnoreCase("Letv")) {
      new MaterialDialog.Builder(MainActivity.this).title("Enable AutoStart")
        .content(
          "Please allow AppName to always run in the background,else our services can't be accessed.")
        .theme(Theme.LIGHT)
        .positiveText("ALLOW")
        .onPositive(new MaterialDialog.SingleButtonCallback() {
          @Override
          public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {

            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.letv.android.letvsafe",
              "com.letv.android.letvsafe.AutobootManageActivity"));
            startActivity(intent);
          }
        })
        .show();
    } else if (Build.BRAND.equalsIgnoreCase("Honor")) {
      new MaterialDialog.Builder(MainActivity.this).title("Enable AutoStart")
        .content(
          "Please allow AppName to always run in the background,else our services can't be accessed.")
        .theme(Theme.LIGHT)
        .positiveText("ALLOW")
        .onPositive(new MaterialDialog.SingleButtonCallback() {
          @Override
          public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.huawei.systemmanager",
              "com.huawei.systemmanager.optimize.process.ProtectActivity"));
            startActivity(intent);
          }
        })
        .show();
    } else if (Build.MANUFACTURER.equalsIgnoreCase("oppo")) {
      new MaterialDialog.Builder(MainActivity.this).title("Enable AutoStart")
        .content(
          "Please allow AppName to always run in the background,else our services can't be accessed.")
        .theme(Theme.LIGHT)
        .positiveText("ALLOW")
        .onPositive(new MaterialDialog.SingleButtonCallback() {
          @Override
          public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
            try {
              Intent intent = new Intent();
              intent.setClassName("com.coloros.safecenter",
                "com.coloros.safecenter.permission.startup.StartupAppListActivity");
              startActivity(intent);
            } catch (Exception e) {
              try {
                Intent intent = new Intent();
                intent.setClassName("com.oppo.safe",
                  "com.oppo.safe.permission.startup.StartupAppListActivity");
                startActivity(intent);
              } catch (Exception ex) {
                try {
                  Intent intent = new Intent();
                  intent.setClassName("com.coloros.safecenter",
                    "com.coloros.safecenter.startupapp.StartupAppListActivity");
                  startActivity(intent);
                } catch (Exception exx) {

                }
              }
            }
          }
        })
        .show();
    } else if (Build.MANUFACTURER.contains("vivo")) {
      new MaterialDialog.Builder(MainActivity.this).title("Enable AutoStart")
        .content(
          "Please allow AppName to always run in the background.Our app runs in background else our services can't be accesed.")
        .theme(Theme.LIGHT)
        .positiveText("ALLOW")
        .onPositive(new MaterialDialog.SingleButtonCallback() {
          @Override
          public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
            try {
              Intent intent = new Intent();
              intent.setComponent(new ComponentName("com.iqoo.secure",
                "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity"));
              startActivity(intent);
            } catch (Exception e) {
              try {
                Intent intent = new Intent();
                intent.setComponent(new ComponentName("com.vivo.permissionmanager",
                  "com.vivo.permissionmanager.activity.BgStartUpManagerActivity"));
                startActivity(intent);
              } catch (Exception ex) {
                try {
                  Intent intent = new Intent();
                  intent.setClassName("com.iqoo.secure",
                    "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager");
                  startActivity(intent);
                } catch (Exception exx) {
                  ex.printStackTrace();
                }
              }
            }
          }
        })
        .show();
    }
  }
Simms answered 6/12, 2017 at 14:21 Comment(6)
Thank you for your answer, I will try it. But please explain what AutoStart is and where to put this code?Ophicleide
if you are copying code from your own project, at least take out all the specificsObservable
@Motassem Jalal and Tim Castelijns ,code is updated with relevant information.Simms
And what if I need to test on an emulator? How can I enable this?Ophicleide
Autostart will have on devices running custom OS(like MiUi),emulators usually run on stock or AOSP Roms which doesn't restrict any app run on background,so no autostart required.Simms
Well that means that the problem is not with autostart, because I'm testing on emulators as well and not triggering any evenOphicleide

© 2022 - 2024 — McMap. All rights reserved.