SDK

Location Services

In this page you'll dive deeper into functionality like using GPS signals to get the user's location or monitor their visits to regions and proximity to BTLE devices.

These services will bring a new level of contextuality to your app, allowing you to create geo-triggers to send notifications or categorize users based on their location behaviour.

The Notificare SDK will ask your users for permission to use location in the background. Users will only allow location updates if you clearly state what you need the location for.

In a real app, you should only ask for location updates after it makes sense to the user. Your app should explain that you are going to ask for location and why your app needs it.

As mentioned in the Implementation page, if you are going to use location services, you must include the following dependency in your app/build.gradle:

dependencies {
    implementation 're.notifica:notificare-location:2.3.0' // make sure you always use the latest version
}

Requesting Permission

Additionally to these manifest permissions, since Android 6 (a.k.a Marshmallow), applications will need to request the user permission to use location.

These permissions need to be requested from an activity in your app, after which you can safely enable location updates.

Since Android 10, there is a distinction between foreground (when app is used) and background (always) location updates. If the user only allows foreground location updates, Geofences will not work and location will only be updated when the app is running.

To make sure the location updates aren't started before the library is ready to be used, you should wait until it is safe to do so, by listening to the OnNotificareReady event.

Let your activity implement the OnNotificareReadyListener interface:

public class MyMainActivity extends ActionBarBaseActivity implements Notificare.OnNotificareReadyListener {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ...more code

        Notificare.shared().addNotificareReadyListener(this);
    }


    @Override
    public void onNotificareReady(NotificareApplicationInfo info) {
        if (!Notificare.shared().hasLocationPermissionGranted()) {
            Log.i(TAG, "permission not granted");
            if (Notificare.shared().didRequestLocationPermission()) {
                // We already asked for location updates before
                if (Notificare.shared().shouldShowRequestPermissionRationale(this)) {
                    // The user previously did not allow location updates.
                    // Here we should show a dialog explaining why the applocation updates
                    new AlertDialog.Builder()
                        .setMessage(R.string.alert_location_permission_rationale)
                        .setTitle(R.string.app_name)
                        .setCancelable(true)
                        .setPositiveButton(R.string.button_ok,
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    Notificare.shared().requestLocationPermission(MyMainActivity.this, LOCATION_PERMISSION_REQUEST_CODE);
                                }
                            })
                        .create()
                        .show();
                }
            } else {
                // This is the first time we ask for location updates
                Notificare.shared().requestLocationPermission(this, LOCATION_PERMISSION_REQUEST_CODE);
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        ...more code

        Notificare.shared().removeNotificareReadyListener(this);
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case LOCATION_PERMISSION_REQUEST_CODE:
                if (Notificare.shared().checkRequestLocationPermissionResult(permissions, grantResults)) {
                    Log.i(TAG, "permission granted");
                    Notificare.shared().enableLocationUpdates();
                }
                break;
        }
    }

}

Once you have implemented the code above, if permission is granted, our library will automatically collect the user location and start monitoring for regions you've created via the dashboard or API.

By default, Notificare.shared().requestLocationPermission will ask the user for background location updates. Likewise, Notificare.shared().hasLocationPermissionGranted() will only return true if the user has allowed background location updates.

To make a distinction between foreground and background location updates permissions, the Notificare SDK offers 2 extra helper methods, hasForegroundLocationPermissionGranted() and shouldShowForegroundRequestPermissionRationale

public void askForegroundLocationPermission() {
    if (!Notificare.shared().hasForegroundLocationPermissionGranted()) {
        Log.i(TAG, "permission not granted");
        if (Notificare.shared().didRequestLocationPermission()) {
            if (Notificare.shared().shouldShowForegroundRequestPermissionRationale(this)) {
                // Here we should show a dialog explaining location updates
                builder.setMessage(R.string.alert_location_permission_rationale)
                        .setTitle(R.string.app_name)
                        .setCancelable(true)
                        .setPositiveButton(R.string.button_location_permission_rationale_ok, (dialog, id) -> Notificare.shared().requestLocationPermission(MainActivity.this, LOCATION_PERMISSION_REQUEST_CODE))
                        .create()
                        .show();
            }
        } else {
            Notificare.shared().requestLocationPermission(this, LOCATION_PERMISSION_REQUEST_CODE);
        }
    } else {
        Log.i(TAG, "foreground location permission granted, we can update location");
        Notificare.shared().enableLocationUpdates();
    }
}

Accessing the user location is as easy as invoking the following method:

Notificare.shared().getCurrentLocation();

If authorized, you will also want to start using GPS data whenever your app is launched, you simply have to add the following in your Intent Receiver:

public class MyIntentReceiver extends DefaultIntentReceiver {

    @Override
    public void onReady() {

        ...more code

        if (Notificare.shared().isLocationUpdatesEnabled()) {

            Notificare.shared().enableLocationUpdates();

        }

        ...more code
    }

    ...more code
}

Using Bluetooth Low-Energy beacons

Once you've implemented GPS location in your app, you can also listen to Bluetooth signals from BTLE beacons in your app. Simply add the following to your /app/build.gradle file:

dependencies {
    implementation 're.notifica:notificare-core:2.3.0'
    implementation 're.notifica:notificare-beacon:2.3.0' // Include this line
}

After that you can enable beacons signals by simply invoking the following method, right after a user has granted permission for tracking location:

public class MyMainActivity extends ActionBarBaseActivity implements Notificare.OnNotificareReadyListener {

    ..more code

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case LOCATION_PERMISSION_REQUEST_CODE:
                if (Notificare.shared().checkRequestLocationPermissionResult(permissions, grantResults)) {
                    Log.i(TAG, "permission granted");
                    Notificare.shared().enableLocationUpdates();
                    Notificare.shared().enableBeacons();
                }
                break;
        }
    }

    ...more code

}

You will also want to enable beacons from your Intent Receiver pretty much the same way you do with location:

public class MyIntentReceiver extends DefaultIntentReceiver {

    @Override
    public void onReady() {

        ...more code

    if (Notificare.shared().isLocationUpdatesEnabled()) {

        Notificare.shared().enableLocationUpdates();
        Notificare.shared().enableBeacons();

        ...more code
    }

    ...more code
}

By doing this, you app will start monitoring for any beacons you inserted via the dashboard or API, in any of your regions.

By default, the app will scan at least every 5 minutes in the background in devices running Android < 8 (Oreo).

If you want your app to be more responsive, e.g. once per minute, you can set the interval yourself. Be aware, though, that a shorter interval will mean more power consumption.

Notificare.shared().enableBeacons(60000)

In Oreo and up, background scans are more limited. First scans of a beacon in a region will come in very quickly, but detection of changes or leaving the beacon's range will take up to 15 minutes when in background. This limitation is posed by Android itself and there is no workaround to do this in background mode.

Additionally, if you wish to get information about the beacons in the vicinity, when your app is in the foreground, here's an example of how you would go about in order to create a list of beacons in a fragment:

public class BeaconsFragment extends Fragment implements BeaconRangingListener {

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        ...more code

        if (Notificare.shared().getBeaconClient() != null) {
            Notificare.shared().getBeaconClient().addRangingListener(this);
        }

        ...more code
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (Notificare.shared().getBeaconClient() != null) {
            Notificare.shared().getBeaconClient().addRangingListener(this);
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        if (Notificare.shared().getBeaconClient() != null) {
            Notificare.shared().getBeaconClient().removeRangingListener(this);
        }
    }

    @Override
    public void onRangingBeacons(final List<NotificareBeacon> notificareBeacons) {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                beaconListAdapter.clear();
                for (NotificareBeacon beacon : notificareBeacons) {
                    beaconListAdapter.add(beacon);
                }
            }
    }


    ...more code
}

This is not a mandatory step in order to use beacons, as geo-triggers created in the dashboard will trigger notifications whenever you are in the vicinity of a beacon, even when your app is not being used.

Disable Location

Pretty much the same way you enable location, you can also stop tracking the user location by invoking the following method:

Notificare.shared().disableLocationUpdates();

Disable Beacons

Although beacons will not work without using location, you can also stop listening to beacon advertising signals by invoking the following method:

Notificare.shared().disableBeacons();