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:

compile 'com.sensorberg.sdk:2.4'
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:
static {		
   com.sensorberg.sdk.internal.transport.RetrofitApiTransport.RESOLVER_BASE_URL = "https://portal.sensorberg-cdn.com";
}

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.

repositories {
    jcenter()
}

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

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

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

Declare your BroadcastReceiver:

<?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

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:

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.

// 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

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:
        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.
     @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

Tip: Pretty ADB log with Android Bluetooth messages hidden

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

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

Tip: Create Your own account when developing

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

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

Read all about it in this blog post.

Tip: Use conversions to measure user interaction.

Read more about conversions in respective Android and iOS blog posts.