Android

SDK migration to new RAILS platform

Starting with SDK 2.2.0-RAILS we’re using newly implemented Sensorberg IoT RAILS platform as a backend. This system upgrade will ensure better functionality, project use cases and simpler user experience.

As a result, a new account must be set up on the Sensorberg IoT platform in order to continue your projects. This applies to every dependency change from non-RAILS version to -RAILS based version.

Simply create an account by visiting portal.sensorberg.com. You have the option to manually move your data (e.g., beacons, campaigns etc) to the new platform and continue to manage your account. 

Alternatively, you may contact our support team at support@sensorberg.com for assistance in the migration procedure.

For more useful knowledge articles, please visit our  Knowledge Center.

The latest current artifact is:

Groovy
compile 'com.sensorberg.sdk:{{ site.latestAndroidSDKRelease }}'

If you are using version 2.2.0 still, you are required to change the base URL in your Application class in a static initializer:

Java
static {
   com.sensorberg.sdk.internal.transport.RetrofitApiTransport.RESOLVER_BASE_URL = "https://portal.sensorberg-cdn.com";
}

Targeting Android 8

The 2.x SDK is not compatible with apps targeting Android 8/Oreo/API level 26 as a target sdk.

If you require Android 8 to be your target SDK, please contact us.

If you want to read up on the topic visit Android 8.0 Behavior Changes.

We do currently not see the need for our customers to target Android 8. If you have specific needs that require Android 8 APIs we would like to hear from you.

How to install the Sensorberg Android SDK

You will need to have the jcenter artifactory in your list of repositories and declare the dependency to our sdk.

Groovy
repositories {
    jcenter()
}

dependencies {
       compile 'com.sensorberg.sdk:android-sdk:{{ site.latestAndroidSDKRelease }}'
}

If your application also uses Geofences, than you must also add the Google Play Services Location module

Groovy
dependencies {
  compile 'com.google.android.gms:play-services-location:<latest version>'
}

Declare your BroadcastReceiver:

XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application>
        <receiver android:name="com.myCompany.MyActionPresenter"
            android:process=".sensorberg"
            android:exported="false">
            <intent-filter>
                <action android:name="com.sensorberg.android.PRESENT_ACTION" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

You cannot add a BroadCastReceiver at runtime! We are using a LocalBroadcastManager to send the broadcast and find the receiver(s).

The BroadcastReceiver is running in another process

You should be aware, that the Sensorberg Android SDK is running in a separate process. The broadcast will be sent in the separate process as well. The intention of the BroadcastReceiver is to present the content of your Action when the app is in background.

Enable the SDK in your Application object and register foreground/background notifications:

Integration

Java
public class DemoApplication extends Application {
    private SensorbergSdk sdk;
    private BackgroundDetector detector;

    static {
      if (BuildConfig.DEBUG){
        Logger.enableVerboseLogging();
      }
    }

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

        sdk = new SensorbergSdk(this, API_KEY);//the context object and your api key.
        sdk.registerEventListener(new SensorbergSdkEventListener() {
            @Override
            public void presentBeaconEvent(BeaconEvent beaconEvent) { //your presentBeaconEvent action.
                showAlert(beaconEvent.getAction(), beaconEvent.trigger);
                Log.i("beaconevent", beaconEvent.getBeaconId().toString());
                Action action = beaconEvent.getAction();
                showAlert(action, beaconEvent.trigger);
            }
        });

        detector = new BackgroundDetector(sdk);
        registerActivityLifecycleCallbacks(detector);
  }
}

It´s now time to implement the BroadcastReceiver:

Java
public class MyActionPresenter extends BroadcastReceiver {
       @Override
       public void onReceive(Context context, Intent intent) {
           Action action = intent.getExtras().getParcelable(Action.INTENT_KEY);
           switch (action.getType()){
               case MESSAGE_URI:
                   UriMessageAction uriMessageAction = (UriMessageAction) action;
                   showNotification(context, action.getUuid().hashCode(), uriMessageAction.getTitle(), uriMessageAction.getContent(), Uri.parse(uriMessageAction.getUri()));
                   break;
               case MESSAGE_WEBSITE:
                   VisitWebsiteAction visitWebsiteAction = (VisitWebsiteAction) action;
                   showNotification(context, action.getUuid().hashCode(), visitWebsiteAction.getSubject(), visitWebsiteAction.getBody(), visitWebsiteAction.getUri());
                   break;
               case MESSAGE_IN_APP:
                   InAppAction inAppAction = (InAppAction) action;
                   showNotification(context, action.getUuid().hashCode(), inAppAction.getSubject(), inAppAction.getBody(), inAppAction.getUri());
                   break;
           }
       }

       private void showNotification(Context context, int id, String title, String content, Uri uri) {
           Intent sendIntent = new Intent();
           sendIntent.setAction(Intent.ACTION_SEND);
           sendIntent.putExtra(Intent.EXTRA_TEXT, title + "\n" + content + "\n" + uri.toString());
           sendIntent.setType("text/plain");

           Notification notification = new NotificationCompat.Builder(context)
                   .setContentIntent(PendingIntent.getActivity(
                           context,
                           0,
                           new Intent(Intent.ACTION_VIEW, uri),
                           PendingIntent.FLAG_UPDATE_CURRENT))
                   .setContentTitle(title)
                   .setContentText(content)
                   .setSmallIcon(R.drawable.ic_launcher)
                   .setAutoCancel(true)
                   .setShowWhen(true)
                   .build();
           NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
           notificationManager.notify(id, notification);
       }

This class receives a broadcast, if the SDK has detected a beacon and successfully resolved an associated Action.

Advertiser ID

There’re several ways of acquiring Advertiser ID which varies per platform. Here we’ll show for the Google ID.

Java
// running on a background thread due to networking
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            AdvertisingIdClient.Info info = AdvertisingIdClient.getAdvertisingIdInfo(context);
            // boot is the instance of ShowcaseBootstrapper instantiated during Application.onCreate()
            MyApp.getInstance().bootStrapper.setAdvertisingIdentifier(info.getId());
        } catch (IOException e) {
            Log.e(TAG, "Could not fetch advertising id", e);
        } catch (GooglePlayServicesNotAvailableException e) {
            Log.e(TAG, "Could not fetch advertising id, Google Play Service not available", e);
        } catch (GooglePlayServicesRepairableException e) {
            Log.e(TAG, "Could not fetch advertising id, Google Play Service need repairing", e);
        } catch (Exception e) {
            Log.e(TAG, "Could not fetch advertising id", e);
        }
    }
}).start();

And of course, if you need to remove it, just call it null

Java
MyApp.getInstance().bootStrapper.setAdvertisingIdentifier(null);

Android 6 Permissions

If you app will target android 6 you will need to prompt the user for location permissions before scanning will work - this should be down in the activity. For a more in-depth discussion please see the Android 6 blog.

In your activity which would use the scanner you need to ask for (location permission) at runtime:

Java
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {
                final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("Functionality limited");
                builder.setMessage("Since location access has not been granted, " +
                        "this app will not be able to discover beacons when in the background.");
                builder.setPositiveButton(android.R.string.ok, null);
                builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                    @Override
                    public void onDismiss(DialogInterface dialog) {
                        ActivityCompat.requestPermissions(DemoActivity.this,
                                new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                MY_PERMISSION_REQUEST_LOCATION_SERVICES);
                    }

                });
                builder.show();
            } else {
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        MY_PERMISSION_REQUEST_LOCATION_SERVICES);
            }
        }

Then you must receive the callback.

Java
     @Override
     public void onRequestPermissionsResult(int requestCode,
                                            String permissions[], int[] grantResults) {
         switch (requestCode) {
             case MY_PERMISSION_REQUEST_LOCATION_SERVICES: {
                 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                     Log.d("Scanner Message", "coarse location permission granted");
                     ((DemoApplication) getApplication()).setLocationPermissionGranted(SensorbergServiceMessage.MSG_LOCATION_SET);
                 } else {
                     ((DemoApplication) getApplication()).setLocationPermissionGranted(SensorbergServiceMessage.MSG_LOCATION_NOT_SET_WHEN_NEEDED);
                 }
                 return;
             }
         }
     }

Please note that if you’re using geofencing the ACCESS_FINE_LOCATION permission is required in your manifest. This is caused by Google Play Services requirements under the hood.

If ACCESS_FINE_LOCATION is not given you won’t receive geofence notifications.

Development Tips

1. Pretty ADB log with Android Bluetooth messages hidden

Use pidcat with grep to show your log and hide the System Bluetooth scan logs:

Bash
pidcat com.myapp.packageIdentifier | grep --invert-match BluetoothLeScanner

2. Create Your own account when developing

As as developer, you can create an account for free at portal.sensorberg.com/register.

3. Use the secret codes broadcastreceiver to add more debugging to your app.

Read all about it in this blog post.

4. Use conversions to measure user interaction

Read more about conversions in respective Android and blog posts.