Add the Campaign SDK to your Android app

The Campaign SDK for Android lets you deliver personalized, interactive push notifications and messages to your Android app.

Requirements

Mobile app compatibility

  • Supported versions: Android 5.0 (API level 21) or later
  • Latest tested version: Android 14 (API level 34)

Development environment

  • Android Studio
  • Firebase libraries: com.google.firebase:firebase-core version 19.0.2 and com.google.firebase:firebase-messaging version 22.0.0
  • Google Mobile Services libraries: com.google.android.gms:play-services-base version 18.3.0

If you are going to use location-based messages, two additional GMS libraries are required. In that case, however, your mobile app will need to ask for an additional user permission - see Device Location Awareness (DLA) for instructions.

  • com.google.android.gms:play-services-location version 21.0.1
  • com.google.android.gms:play-services-maps version 17.0.1

For example, see configuration files from our demo app:

Before you begin

Before you begin, make sure the following conditions are met.

  • Your mobile app is registered and implemented (this is necessary for testing and creating notifications).
  • In your mobile app, Firebase Cloud Messaging is enabled.

🚧

Attention

The legacy Google API key authentication will be deprecated on June 20th, 2024. If the push service type in your mobile app is set to "FCM", you must update to the latest Firebase Cloud Messaging API (v1).

  • The install location of your app is set to the internal memory of a mobile device, not to external storage cards.
  • Your company has an active Acoustic Campaign subscription.
  • You have a developer account for Acoustic Campaign.
  • Your mobile app has been added to Acoustic Campaign. For instructions, see Add and configure mobile developer apps.
  • In Acoustic Campaign, mobile push notifications are enabled for your mobile app. For more information, see Get started with mobile campaign.

Initial setup

There are two ways to add Campaign SDK to your Gradle project:

  • With the help of a dependency manager (Maven Central) - recommended
  • Manually by copying our AAR file

Always use the latest release of Campaign SDK for Android.

Here is how to add Campaign SDK to your Android app using Maven Central.

  1. In your project-level build.gradle file, add mavenCentral() as a new repository for global use in the project.
repositories { google() mavenCentral() maven { url "https://plugins.gradle.org/m2/" } maven { url "https://maven.google.com" name 'Google' } // Use line below for latest beta version or remove to use production version maven { url "https://s01.oss.sonatype.org/content/repositories/staging" } }
  1. In the app-level build.gradle file, edit the Dependencies section to support Maven, Google Play Services and Firebase. Note that the plus sign in our example stands for the latest version of the SDK. You can enter a version number instead.
// Example // Required base libraries implementation "io.github.go-acoustic:acoustic-mobile-push-android-sdk:+" implementation "io.github.go-acoustic:acoustic-mobile-push-android-inapp:+" implementation "io.github.go-acoustic:acoustic-mobile-push-android-inbox:+" // Use the following for the plugins needed implementation "io.github.go-acoustic:acoustic-mobile-push-android-calendar:+" implementation "io.github.go-acoustic:acoustic-mobile-push-android-carousel:+" implementation "io.github.go-acoustic:acoustic-mobile-push-android-displayweb:+" implementation "io.github.go-acoustic:acoustic-mobile-push-android-snooze:+" /* If you are going to support location services, add this. */ implementation 'com.google.android.gms:play-services-location:+'
  1. Build your project.

The manual steps are as follows:

  1. Create the libs/ directory next to your src/ directory if it is not there yet.
  2. Copy the Campaign SDK AAR file to the libs/ directory.
  3. In the project-level build.gradle file, declare the following dependencies. Note that the versions of GMS and Firebase may or may not be the same.
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.aar']) implementation 'com.google.android.gms:play-services-base:<play-services version>' implementation 'com.google.firebase:firebase-messaging:<play-services version>' /* If you are going to support location services, add this. */ implementation 'com.google.android.gms:play-services-location:<play-services version>'

Here is an example from our demo app:

com.google.android.gms:play-services-base: 11.8.0 com.google.firebase:firebase-messaging: 11.8.0 /* If you are going to support location services, add this. */ com.google.android.gms:play-services-location: 11.8.0

Updating the SDK configuration

Replacing the default application class

By default, the application class in Campaign SDK is set to co.acoustic.mobile.push.sdk.api.MceApplication. If you want to replace it, set android:name in the AndroidManifest.xml file and add android:name to tools:replace.

If you implement your own application class, replace YOUR_APP_PACKAGE_NAME with the name of your package.

You can use the following as a template for your application class:

package YOUR_APP_PACKAGE_NAME;
import android.annotation.TargetApi;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import co.acoustic.mobile.push.sdk.api.MceApplication;
import co.acoustic.mobile.push.sdk.api.MceSdk;
import co.acoustic.mobile.push.sdk.api.notification.NotificationsPreference;
public class MyApplication extends MceApplication {
  public void onCreate() {
    super.onCreate();
    if (Build.VERSION.SDK_INT & gt; = Build.VERSION_CODES.O) {
      createNotificationChannel(getApplicationContext());
    }
  }
  @
  TargetApi(26) private static void createNotificationChannel(Context context) {
    String MY_SAMPLE_NOTIFICATION_CHANNEL_ID = context.getString(R.string.notif_channel_id);
    NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    NotificationChannel channel = notificationManager.getNotificationChannel(MY_SAMPLE_NOTIFICATION_CHANNEL_ID);
    if (channel == null) {
      CharSequence name = context.getString(R.string.notif_channel_name);
      String description = context.getString(R.string.notif_channel_description);
      int importance = NotificationManager.IMPORTANCE_HIGH;
      channel = new NotificationChannel(MY_SAMPLE_NOTIFICATION_CHANNEL_ID, name, importance);
      channel.setDescription(description);
      NotificationsPreference notificationsPreference = MceSdk.getNotificationsClient().getNotificationsPreference();
      notificationsPreference.setNotificationChannelId(context, MY_SAMPLE_NOTIFICATION_CHANNEL_ID);
      notificationManager.createNotificationChannel(channel);
    }
  }
}

The application class refers to the following strings that you will need to add to strings.xml.

<string name="notif_channel_id">my-notification-channel</string>
<string name="notif_channel_name">My sample notification channel</string>
<string name="notif_channel_description">My sample notification channel description</string>

Setting a default notification channel

You must set a default notification channel. Add the following to the <application> node in AndroidManifest.xml:

<meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="@string/notif_channel_id" /

If you intend to send push notifications of the DIAL or URL type, add the following to AndroidManifest.xml at the same level as the node:

<queries>
	<!-- Required for handling notifications that open the devices dialer app -->
	<intent>
		<action android:name="android.intent.action.DIAL" />
	</intent>
	<!-- Required for handling notifications that open URLs -->
	<intent>
		<action android:name="android.intent.action.VIEW"/>
		<data android:scheme="https"/>
	</intent>
</queries>

You can selectively remove any of these nodes. If you remove a node and then send a push notification using the removed support, the push notification will only open the app. It will not invoke the expected action. This requirement was introduced in Android 12 (API level 31).

Customizing backup preferences

To prevent unexpected behaviour, we recommend excluding Campaign SDK files from your app backup. For more information, see Disable or modify automated backups for your Android app.

Enabling database encryption

You can encrypt the Campaign SDK database. For more information, see Android SDK database encryption.

Setting Campaign SDK properties

You can set Campaign SDK properties using either of the following:

  • The MceConfig.json properties file. The most common way is to copy MceConfig.json from one of our sample apps into your app/src/main/assets directory. We also provide an API option. For details, see Configuration (MceConfig.json).
  • The MceSdkConfiguration object. You must ensure that the application class is not MceApplication and that you call the MceApplication init method with the MceSdkConfiguration object. If you do not set a config property, a default value is used. For more information, see Modify the SDK initialization control for alternate Android integration.

Here is an example of theMceSdkConfiguration object:

import co.acoustic.mobile.push.sdk.api.MceApplication;
import co.acoustic.mobile.push.sdk.api.MceSdkConfiguration;
import co.acoustic.mobile.push.sdk.api.SdkInitLifecycleCallbacks;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class SampleApplication extends Application {

  @Override
  public void onCreate() {
    super.onCreate();
    String appKey = "<YOUR APP KEY>";
    MceSdkConfiguration mceSdkConfiguration = new MceSdkConfiguration(appKey, "");

    mceSdkConfiguration.setBaseUrl("<YOUR BASE URL>");

    mceSdkConfiguration.setMessagingService(MceSdkConfiguration.MessagingService.fcm);
    mceSdkConfiguration.setGroupNotificationsByAttribution(true);

    mceSdkConfiguration.setLogBufferSize(10);
    mceSdkConfiguration.setLogFile(true);
    mceSdkConfiguration.setLogIterationDurationInHours(1);
    mceSdkConfiguration.setLogLevel(Logger.LogLevel.error);

    mceSdkConfiguration.setMetricTimeInterval(300);

    mceSdkConfiguration.setSessionsEnabled(true);
    mceSdkConfiguration.setSessionTimeout(20);
    mceSdkConfiguration.setUseFileImageCache(true);
    mceSdkConfiguration.setUseInMemoryImageCache(true);
    mceSdkConfiguration.setFileImageCacheCapacityInMB(200);
    mceSdkConfiguration.setInMemoryImageCacheCapacityInMB(20);

    MceSdkConfiguration.LocationConfiguration.SyncConfiguration syncConfiguration = mceSdkConfiguration.getLocationConfiguration().getSyncConfiguration();
    try {
      syncConfiguration.setProviderPreferences(new JSONArray("[\"gps\", \"network\"]"));
    } catch (JSONException e) {}
    syncConfiguration.setLocationResponsiveness(300);
    syncConfiguration.setSyncInterval(300);
    syncConfiguration.setSyncRadius(100000);
    syncConfiguration.setMinLocationsForSearch(1);
    syncConfiguration.setMaxLocationsForSearch(20);

    MceSdkConfiguration.LocationConfiguration.IBeaconConfiguration iBeaconConfiguration = mceSdkConfiguration.getLocationConfiguration().getiBeaconConfiguration();
    iBeaconConfiguration.setUuid("<YOUR UUID>");
    iBeaconConfiguration.setBeaconForegroundScanDuration(5);
    iBeaconConfiguration.setBeaconForegroundScanInterval(30);
    iBeaconConfiguration.setBeaconBackgroundScanDuration(30);
    iBeaconConfiguration.setBeaconBackgroundScanInterval(300);

    MceApplication.init(this, mceSdkConfiguration, new SdkInitLifecycleCallbacks() {
      @Override
      public void handleMetadata(Bundle bundle) {

      }

      @Override
      public void onPluginActionLoad(JSONObject jsonObject) {

      }

      @Override
      public void onPluginNotificationTypeLoad(JSONObject jsonObject) {

      }

      @Override
      public void onStart(MceSdkConfiguration mceSdkConfiguration) {

      }

      @Override
      public void onSdkReinitializeNeeded(Context context) {

      }
    });
  }
}

Additional options

Monitoring events

Implement MceBroadcastReceiver if you want to monitor events in Campaign SDK, such as SDK registration, FCM registration, and notification received. Create a new MceBroadcastReceiver implementation class. The following example shows how to create a new MceBroadcastReceiver implementation class with FCM.

package YOUR_APP_PACKAGE;
import android.content.Context;
import java.util.Date;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import co.acoustic.mobile.push.sdk.api.MceBroadcastReceiver;
import co.acoustic.mobile.push.sdk.api.attribute.AttributesOperation;
import co.acoustic.mobile.push.sdk.api.event.Event;
import co.acoustic.mobile.push.sdk.api.notification.NotificationDetails;
import co.acoustic.mobile.push.sdk.location.MceLocation;
import co.acoustic.mobile.push.sdk.api.broadcast.EventBroadcastUtil;
import java.util.List;
public class MyMceBroadcastReceiver extends MceBroadcastReceiver {
  @Override
  public void onSdkRegistered(Context context) {
    // Handle the SDK registration event
    // context - The application context
  }
  @Override
  public void onMessagingServiceRegistered(Context context) {
    // Handle the FCM registration event
    // context - The application context
  }
  @Override
  public void onSdkRegistrationChanged(Context context) {
    // context - The application context
  }
  @Override
  public void onSdkRegistrationUpdated(Context context) {
    // context - The application context
  }
  @Override public void onMessage(Context context, NotificationDetails notificationDetails, Bundle extraPayload) {
    // Handle the notification received event
    // context - The application context
    // notificationDetails - The received notification
    // extraPayload- Additional payload that arrived with the notification
  }
  @Override
  public void onSessionStart(Context context, Date sessionStartDate) {
    // context - The application context
    // sessionStartDate- The new session start time
  }
  @Override
  public void onSessionEnd(Context context, Date sessionEndDate, long sessionDurationInMinutes) {
    // context - The application context
    // sessionEndDate- The session end time
    // sessionDurationInMinutes - The session duration in minutes 
  }
  @Override
  public void onNotificationAction(Context context, Date actionTime, String pushType, String actionType, String actionValue) {
    // context - The application context 
    // actionTime- The time the action was clicked on
    // pushType - always "simple"
    // actionType - The type of the action
    // actionValue - the value of the "value" key in the payload.
  }
  @Override
  public void onAttributesOperation(Context context, AttributesOperation attributesOperation) {
    // context - The application context
    // attributesOperation - The operation that was executed
  }
  @Override
  public void onEventsSend(Context context, List < Event > list) {
    // context - The application context
    // events- The events that were sent 
  }
  @Override
  public void onIllegalNotification(Context context, Intent intent) {
    // context - The application context
    // intent- The intent that contains the illegal notification
  }
  @Override
  public void onNonMceBroadcast(Context context, Intent intent) {
    // context - The application context
    // intent- The intent that contains the non MCE broadcast
  }
  /**
   * This method is called when a location event occurs
   * @param location The related location
   * @param locationType The related location type
   * @param locationEventType The related location event type
   */
  public void onLocationEvent(Context context, MceLocation location, LocationType locationType, LocationEventType locationEventType) {
    // do something interesting with the location event
  };
  /**
   * This method is called when the device location is updated
   * @param context The application's context
   * @param location The device location
   */
  public void onLocationUpdate(Context context, Location location) {
    // do something interesting with the location update
  }
  @Override
  public void onReceive(Context context, Intent intent) {
    Log.i(getClass().getSimpleName(), "Received intent: " + intent.toString());
    if (intent == null || intent.getAction() == null) {
      return;
    }
    try {
      EventBroadcastUtil.handleBroadcast(context, intent, this);
    } catch (Throwable t) {
      Log.e(getClass().getSimpleName(), "Unexpected error on receive: ", t);
    }
  }
  @Override
  public void onC2dmError(Context context, String errorId) {
    Log.i(getClass().getSimpleName(), "C2DM errorId: " + errorId);
  }
}

After implementing MceBroadcastReceiver, add it to AndroidManifest.xml below the <application> node.

<receiver android:name=".MyMceBroadcastReceiver">
	<intent-filter>
		<action android:name="co.acoustic.mobile.push.sdk.NOTIFIER" />
	</intent-filter>
</receiver>

Enabling multiple FCM providers

If your app must support push notifications from two sources (Acoustic Campaign and another source), then you can implement the following code to allow both of the push services to co-exist within your app.

  1. Add a class to support FCM messages. For example, create class MyFirebaseMessagingService extends FirebaseMessagingService.
  2. Add import co.acoustic.mobile.push.sdk.api.fcm.FcmApi;.
  3. Add the following code to the public void onMessageReceived(RemoteMessage remoteMessage) method.
If(FcmApi.isFcmMessage(remoteMessage)) {
  FcmApi.handleMceFcmMessage(getApplicationContext(), remoteMessage);
  return;
}
else {
  // this is not sdk message. Handle it here
}
  1. Replace FcmMessagingService in your manifest with your FCM message handler class. In this example, it’s MyFirebaseMessagingService.
<!-- FCM Messages -->
<
service
android: name = ".MyFirebaseMessagingService" >
  <
  !--replace android: name = "co.acoustic.mobile.push.sdk.fcm.FcmMessagingService"
With
android: name = ".MyFirebaseMessagingService"
  -->
  <
  intent - filter >
  <
  action android: name = "com.google.firebase.MESSAGING_EVENT" / >
  <
  /intent-filter> <
  /service>

When an FCM notification arrives, the following happens:

  • The OS calls MyFirebaseMessagingService::onMessageReceived().
  • The code checks if the message is from Acoustic. A message from Acoustic has “alert”: in the sub-document message payload.
  • If the payload is from Acoustic, the SDK handles the message and returns it. Otherwise, the message is handled by MyFirebaseMessagingService.

For more information, see Android SDK messaging API.