Below is a list of steps neccessary to hook-up your Android app with the proxi.cloud panel.

Remember to set-up your application in the panel before integrating the SDK into your app. Go to https://panel.proxi.cloud and generate an API key for your app.

You will need to have the jcenter artifactory in your list of repositories and declare the dependency to our sdk. Although this is not neccessary for integration, you can view the entire source code on gitlab.

  repositories {
      jcenter()
  }

  dependencies {
      implementation 'cloud.proxi.sdk:cloud.proxi.sdk:1.6.4'
  }

Make sure that you are using the latest avaliable version on jcenter:

Manifest declarations

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=".proxiCloud"
             android:exported="false">
             <intent-filter>
                  <action android:name="cloud.proxi.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 Proxi.cloud 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.

Please note that ACCESS_FINE_LOCATION is required in your manifest. This is caused by Google Play Services requirements under the hood. If this permission is not given you won’t receive geofence notifications, but you can still receive those generated by beacons, provided your app holds ACCESS_COARSE_LOCATION permission

ACCESS_WIFI_STATE and CHANGE_WIFI_STATE are neccessary to take advantage of the WiFi triggers

Integration

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

  public class DemoApplication extends Application {
      public Context activityContext; //set this context in Activity

      private ProxiCloudSdk sdk;
      private BackgroundDetector detector;
   

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

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

          sdk = new ProxiCloudSdk(this, API_KEY);//the context object and your api key.
          sdk.registerEventListener(new ProxiCloudSdkEventListener() {
              @Override
              public void presentBeaconEvent(BeaconEvent beaconEvent) { //your presentBeaconEvent action.
                Action action = beaconEvent.getAction();
                ActionType type = action.getType();

                switch (type) {
                    case MESSAGE_URI:
                        UriMessageAction uriMessageAction = (UriMessageAction) action;
                        showAlert(uriMessageAction.getUuid().hashCode(), uriMessageAction.getTitle(),
                                uriMessageAction.getContent(), Uri.parse(uriMessageAction.getUri()), action);
                        break;
                    case MESSAGE_WEBSITE:
                        VisitWebsiteAction visitWebsiteAction = (VisitWebsiteAction) action;
                        showAlert(visitWebsiteAction.getUuid().hashCode(), visitWebsiteAction.getSubject(),
                                visitWebsiteAction.getBody(), visitWebsiteAction.getUri(), action);
                        break;
                    case MESSAGE_IN_APP:
                        InAppAction inAppAction = (InAppAction) action;
                        showAlert(inAppAction.getUuid().hashCode(), inAppAction.getSubject(),
                                inAppAction.getBody(), inAppAction.getUri(), action);
                        break;
                }
              }
          });

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

    private void showAlert(int hashCode, String subject, String body, Uri uri, Action action) {

        if (activityContext == null) {
            MyActionPresenter.showNotification(this, hashCode, subject, body, uri, action);
            return;
        }

        AlertDialog.Builder dialog = new AlertDialog.Builder(activityContext)
                .setTitle(subject)
                .setMessage(body)
                .setPositiveButton("open url", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Toast
                            .makeText(getApplicationContext(), "open URL " + uri.toString(), Toast.LENGTH_SHORT)
                            .show();
                    }
                });
        dialog.show();
    }
  }

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) {

       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.

If your app targets api >= 26 (Android 8.0) don’t forget to include support for notification channels. Without this notifications won’t show.

https://developer.android.com/training/notify-user/channels

public class DemoApplication extends Application {
    @Override
    public void onCreate() {
        ...
        initNotificationChannels();
        ...
    }

    @TargetApi(Build.VERSION_CODES.O)
    public static void initNotificationChannels(final Context context) {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationChannel channel = new NotificationChannel(
                    "demo_channel", "Proxi.Cloud sdk events", NotificationManager.IMPORTANCE_DEFAULT);
            channel.setDescription("Display resolved action events");
            notificationManager.createNotificationChannel(channel);
        }
    }
}

Conversions

The SDK now allows to report back the success of push notification delivery and whether the user tapped on it. Find out how to do it here

Play services

Make sure that you compile in a correct version of Google play services (if you are not using them already) - this is required for geofencing and other subcomponents to work

  compile 'com.google.android.gms:play-services-location:11.6.0'
  compile 'com.google.android.gms:play-services-ads:11.6.0'

Please instert this into your app manifest within the application tag

  <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />

Add support for Advertising identifier

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);
              // sdk is the instance of proxi.cloud sdk instantiated during Application.onCreate()
              MyApp.getInstance().sdk.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().sdk.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 this page.

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 and pass it to the SDK via the .sendLocationFlagToReceiver() method.

  @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()).sdk
                            .sendLocationFlagToReceiver(ProxiCloudServiceMessage.MSG_LOCATION_SET);
                } else {
                    ((DemoApplication) getApplication()).sdk
                            .sendLocationFlagToReceiver(ProxiCloudServiceMessage.MSG_LOCATION_NOT_SET_WHEN_NEEDED);
                }
            }
        }
  }