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);
    }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Motassem Jalal
  • 1,254
  • 1
  • 22
  • 46
  • 1
    Can you post the code setting your IntentService and the IntentService itself, please? –  Dec 06 '17 at 19:49
  • 1
    @ThomasStevens please check updates – Motassem Jalal Dec 07 '17 at 08:10
  • 1
    And the GeoReceiver class extends IntentService? –  Dec 07 '17 at 09:46
  • 1
    @ThomasStevens no, my GeoReceiver extends BroadcastReceiver and GeoIntentService extends IntentService – Motassem Jalal Dec 07 '17 at 09:49
  • 2
    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. –  Dec 07 '17 at 09:53
  • 1
    @ThomasStevens a lot of tutorials suggested using receivers. See this codelab https://codelabs.developers.google.com/codelabs/background-location-updates-android-o/index.html?index=..%2F..%2Findex#5 However, as mentioned I tried to use IntentService as well but nothing happend. – Motassem Jalal Dec 07 '17 at 09:56
  • 1
    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. [broadcasts](https://developer.android.com/guide/components/broadcasts.html) –  Dec 07 '17 at 10:02
  • 1
    @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.getGeofencingClient – Motassem Jalal Dec 07 '17 at 10:34
  • 1
    mGeofencingClient = LocationServices.getGeofencingClient(getActivity()); Is what I have in the onResume() of my location fragment –  Dec 07 '17 at 11:01
  • I'm using the context constructor like this: mGeofencingClient = LocationServices.getGeofencingClient(App.getInstance()). Where App is my application class. – Motassem Jalal Dec 07 '17 at 11:37
  • If you are constructing this in an activity or fragment it is best to use the provided context, rather than instantiate a new App –  Dec 07 '17 at 15:10
  • is this ***getGeofencePendingIntent()*** running in service ? – Itapox Dec 13 '17 at 01:23
  • @Itapox 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. – Motassem Jalal Dec 13 '17 at 09:47

1 Answers1

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();
    }
  }
Sachin Varma
  • 2,175
  • 4
  • 28
  • 39