Integrate Campaign SDK with your app integrating-campaign-sdk-into-the-mobile-application
To get Campaign SDK (previously known as Neolane SDK), contact Adobe Customer Care.
To learn more on the different Android and iOS versions supported, refer to the Compatibility matrix.
You can find below the integration steps for Campaign SDK.
-
In Android: the neolane_sdk-release.aar file must be linked to the project.
The following permission grants access to the Adobe Campaign server:
code language-none Neolane.getInstance().setIntegrationKey("your Adobe mobile app integration key"); Neolane.getInstance().setMarketingHost("https://yourMarketingHost:yourMarketingPort/"); Neolane.getInstance().setTrackingHost("https://yourTrackingHost:yourTrackingPort/");
The following permission allows you to recover a mobile’s unique ID:
code language-none <uses-permission android:name="android.permission.READ_PHONE_STATE" />
From version 1.0.24 of the SDK, this permission is only used for versions older than Android 6.0.
From version 1.0.26 of the SDK, this permission is no longer used.
-
In iOS: the libNeolaneSDK.a and Neolane_SDK.h files must be linked to the project. From version 1.0.24 of the SDK, the option ENABLE_BITCODE is activated.
note note NOTE For version 1.0.25 of the SDK, the four architectures are available in the Neolane_SDK.h file.
To integrate Campaign SDK into the mobile application, the functional administrator must provide the following information to the developer:
-
An integration key: to enable the Adobe Campaign platform to identify the mobile application.
note note NOTE This integration key is entered in the Adobe Campaign console, in the Information tab of service dedicated to the mobile application. Refer to Configuring a mobile application in Adobe Campaign. -
A tracking URL: that matches the address of the Adobe Campaign tracking server.
-
A marketing URL: to enable the collection of subscriptions.
-
In Android:
code language-none Neolane.getInstance().setIntegrationKey("your Adobe mobile app integration key"); Neolane.getInstance().setMarketingHost("https://yourMarketingHost:yourMarketingPort/"); Neolane.getInstance().setTrackingHost("https://yourTrackingHost:yourTrackingPort/");
-
In iOS:
code language-none Neolane_SDK *nl = [Neolane_SDK getInstance]; [nl setMarketingHost:strMktHost]; [nl setTrackingHost:strTckHost]; [nl setIntegrationKey:strIntegrationKey];
The registration function enables you to:
-
send the notification ID or push ID (deviceToken for iOS and registrationID for Android) to Adobe Campaign.
-
recover the reconciliation key or userKey (email or account number, for instance)
-
In Android:
code language-none void registerInNeolane(String registrationId, String userKey, Context context) { try{ Neolane.getInstance().registerDevice(registrationToken, userKey, null, context); } catch (NeolaneException e){ //... } catch (IOException e){ //... } }
If you use FCM (Firebase Cloud Messaging), we advise you use the registerDevice function when calling the onTokenRefresh function to notify Adobe Campaign of the change in the user’s mobile device token.
code language-none public class NeoTripFirebaseInstanceIDService extends FirebaseInstanceIdService { @Override public void onTokenRefresh() { String registrationToken = FirebaseInstanceId.getInstance().getToken(); NeolaneAsyncRunner neolaneAs = new NeolaneAsyncRunner(Neolane.getInstance()); ... ... // Neolane Registration neolaneAs.registerDevice(registrationToken, userKey, additionnalParam, this, new NeolaneAsyncRunner.RequestListener() { public void onComplete(String e, Object state) { ... } public void onNeolaneException(NeolaneException e, Object state) { ... } public void onIOException(IOException e, Object state) { ... } }); ... ... } }
-
In iOS:
code language-none // Callback called on successful registration to the APNs - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { // Pass the token to Adobe Campaign Neolane_SDK *nl = [Neolane_SDK getInstance]; [nl registerDevice:tokenString:self.userKey:dic]; }
-
In Android:
Tracking functions allow you to track notification activations (opens) and notification displays (screenshot).
To track the notification display (done by calling the notifyReceive function of the SDK), follow the implementation below. Note that if you use FCM (Firebase Cloud Messaging), we advise you to use the notifyReceive function when the onMessageReceived function is called by the Android system.
code language-none package com.android.YourApplication; import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; public class YourApplicationFirebaseMessagingService extends FirebaseMessagingService { private static final String TAG = "MyFirebaseMsgService"; @Override public void onMessageReceived(RemoteMessage message) { Log.d(TAG, "Receive message from: " + message.getFrom()); Map<String,String> payloadData = message.getData(); final Bundle extras = new Bundle(); final Iterator<Entry<String, String>> iter = payloadData.entrySet().iterator(); while(iter.hasNext()) { final Entry<String, String> entry =iter.next(); extras.putString(entry.getKey(), entry.getValue()); } SharedPreferences settings = this.getSharedPreferences(YourApplicationActivity.APPLICATION_PREF_NAME, Context.MODE_PRIVATE); String mesg = payloadData.get("_msg"); String title = payloadData.get("title"); String url = payloadData.get("url"); String messageId = payloadData.get("_mId"); String deliveryId = payloadData.get("_dId"); YourApplicationActivity.handleNotification(this, mesg, title, url, messageId, deliveryId, extras); } }
code language-none public static void handleNotification(Context context, String message, String title, String url, String messageId, String deliveryId, Bundle extras){ if( message == null ) message = "No Content"; if( title == null ) title = "No title"; if( url == null ) url = "https://www.tripadvisor.fr"; int iconId = R.drawable.notif_neotrip; // notify Neolane that a notification just arrived SharedPreferences settings = context.getSharedPreferences(NeoTripActivity.APPLICATION_PREF_NAME, Context.MODE_PRIVATE); Neolane.getInstance().setIntegrationKey(settings.getString(NeoTripActivity.APPUUID_NAME, NeoTripActivity.DFT_APPUUID)); Neolane.getInstance().setMarketingHost(settings.getString(NeoTripActivity.SOAPRT_NAME, NeoTripActivity.DFT_SOAPRT)); Neolane.getInstance().setTrackingHost(settings.getString(NeoTripActivity.TRACKRT_NAME, NeoTripActivity.DFT_TRACKRT)); NeolaneAsyncRunner nas = new NeolaneAsyncRunner(Neolane.getInstance()); nas.notifyReceive(Integer.valueOf(messageId), deliveryId, new NeolaneAsyncRunner.RequestListener() { public void onNeolaneException(NeolaneException arg0, Object arg1) {} public void onIOException(IOException arg0, Object arg1) {} public void onComplete(String arg0, Object arg1){} }); if (yourApplication.isActivityVisible()) { Log.i("INFO", "The application has the focus" ); ... } else { // notification creation : NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); Notification notification; // Activity to start : Intent notifIntent = new Intent(context.getApplicationContext(), NotificationActivity.class); notifIntent.putExtra("notificationText", message); notifIntent.putExtra(NotificationActivity.NOTIFICATION_URL_KEYNAME, url); notifIntent.putExtra("_dId", deliveryId); notifIntent.putExtra("_mId", messageId); notifIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent contentIntent = PendingIntent.getActivity(context, 1, notifIntent, PendingIntent.FLAG_UPDATE_CURRENT); notification = new Notification.Builder(context) .setContentTitle(title) .setContentText(message) .setSmallIcon(iconId) .setContentIntent(contentIntent) .build(); // launch the notification : notification.flags |= Notification.FLAG_AUTO_CANCEL; notificationManager.notify(Integer.valueOf(messageId), notification); } }
Here is an implementation example for tracking a notification open (executed by calling the notifyOpening function of the SDK). The NotificationActivity class corresponds to the one used to create the notifIntent object in the previous example.
code language-none public class NotificationActivity extends Activity { public void onCreate(Bundle savedBundle) { [...] Bundle extra = getIntent().getExtras(); if (extra != null) { // reinit the acc sdk SharedPreferences settings = getSharedPreferences(NeoTripActivity.APPLICATION_PREF_NAME, Context.MODE_PRIVATE); Neolane.getInstance().setIntegrationKey(settings.getString(NeoTripActivity.APPUUID_NAME, NeoTripActivity.DFT_APPUUID)); Neolane.getInstance().setMarketingHost(settings.getString(NeoTripActivity.SOAPRT_NAME, NeoTripActivity.DFT_SOAPRT)); Neolane.getInstance().setTrackingHost(settings.getString(NeoTripActivity.TRACKRT_NAME, NeoTripActivity.DFT_TRACKRT)); // Get the messageId and the deliveryId to do the tracking String deliveryId = extra.getString("_dId"); String messageId = extra.getString("_mId"); if (deliveryId != null && messageId != null) { try { Neolane.getInstance().notifyOpening(Integer.valueOf(messageId), Integer.valueOf(deliveryId)); } catch (NeolaneException e) { // ... } catch (IOException e) { // ... } } } } }
-
In iOS:
The tracking function allows you to track when notifications are activated (opens).
code language-none (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)launchOptions fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { if( launchOptions ) { // Retrieve notification parameters here ... // Track application opening Neolane_SDK *nl = [Neolane_SDK getInstance]; [nl track:launchOptions:NL_TRACK_CLICK]; } ... completionHandler(UIBackgroundFetchResultNoData); }
note note NOTE From version 7.0, once the application:didReceiveRemoteNotification:fetchCompletionHandler
function is implemented, the operating system only calls this function. Theapplication:didReceiveRemoteNotification
function is therefore not called.
iOS lets you send silent notifications, a notification or data which will be directly sent to a mobile application without displaying it. Adobe Campaign allows you to track them.
To track your silent notification, follow the example below:
code language-none |
---|
|
note note |
---|
NOTE |
Please note that this is exclusive to iOS. |
In iOS, the delegate protocol allows you to get the result of the registerDevice call and can be used to know if an error occurred during registration.
The registerDeviceStatus prototype is:
code language-none |
---|
|
Status allows you to know if a registration succeeded or if an error occurred.
ErrorReason provides you with more information on the errors that occurred. For more information on available errors and their descriptions, refer to the table below.
table 0-row-3 1-row-3 2-row-3 3-row-3 4-row-3 5-row-3 6-row-3 | ||
---|---|---|
Status | Description | ErrorReason |
ACCRegisterDeviceStatusSuccess | Registration Succeeded | EMPTY |
ACCRegisterDeviceStatusFailureMarketingServerHostnameEmpty | The ACC marketing server hostname is empty or not set. | EMPTY |
ACCRegisterDeviceStatusFailureIntegrationKeyEmpty | The integration key is empty or not set. | EMPTY |
ACCRegisterDeviceStatusFailureConnectionIssue | Connection issue with ACC | More information (in OS current language) |
ACCRegisterDeviceStatusFailureUnknownUUID | The provided UUID (integration key) is unknown. | EMPTY |
ACCRegisterDeviceStatusFailureUnexpectedError | Unexpected error returned to ACC server. | The error message returned to ACC. |
Neolane_SDKDelegate protocol and registerDeviceStatus delegate definition is as follows:
code language-none |
---|
|
To implement registerDeviceStatus delegate, follow these steps:
-
Implement the setDelegate during the SDK initialization.
code language-none // AppDelegate.m ... ... - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... ... // Get the stored settings NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *strMktHost = [defaults objectForKey:@"mktHost"]; NSString *strTckHost = [defaults objectForKey:@"tckHost"]; NSString *strIntegrationKey = [defaults objectForKey:@"integrationKey"]; userKey = [defaults objectForKey:@"userKey"]; // Configure Neolane SDK on first launch Neolane_SDK *nl = [Neolane_SDK getInstance]; [nl setMarketingHost:strMktHost]; [nl setTrackingHost:strTckHost]; [nl setIntegrationKey:strIntegrationKey]; [nl setDelegate:self]; // HERE ... ... }
-
Add the protocol in the @interface of your class.
code language-none // AppDelegate.h #import <UIKit/UIKit.h> #import <CoreLocation/CoreLocation.h> #import "Neolane_SDK.h" @class LandingPageViewController; @interface AppDelegate : UIResponder <UIApplicationDelegate, CLLocationManagerDelegate, Neolane_SDKDelegate> { CLLocationManager *locationManager; NSString *userKey; NSString *mktServerUrl; NSString *tckServerUrl; NSString *homeURL; NSString *strLandingPageUrl; NSTimer *timer; }
-
Implement the delegate in the AppDelegate.
code language-none // AppDelegate.m #import "AppDelegate.h" #import "Neolane_SDK.h" #import "LandingPageViewController.h" #import "RootViewController.h" ... ... - (void) registerDeviceStatus: (ACCRegisterDeviceStatus) status :(NSString *) errorReason { NSLog(@"registerStatus: %lu",status); if ( errorReason != nil ) NSLog(@"errorReason: %@",errorReason); if( status == ACCRegisterDeviceStatusSuccess ) { // Registration successful ... ... } else { // An error occurred NSString *message; switch ( status ){ case ACCRegisterDeviceStatusFailureUnknownUUID: message = @"Unkown IntegrationKey (UUID)"; break; case ACCRegisterDeviceStatusFailureMarketingServerHostnameEmpty: message = @"Marketing URL not set or Empty"; break; case ACCRegisterDeviceStatusFailureIntegrationKeyEmpty: message = @"Integration Key not set or empty"; break; case ACCRegisterDeviceStatusFailureConnectionIssue: message = [NSString stringWithFormat:@"%@ %@",@"Connection issue:",errorReason]; break; case ACCRegisterDeviceStatusFailureUnexpectedError: default: message = [NSString stringWithFormat:@"%@ %@",@"Unexpected Error",errorReason]; break; } ... ... } } @end
The variables let you define mobile application behavior after receiving a notification. These variables must be defined in the mobile application code and in the Adobe Campaign console, in the Variables tab in the dedicated mobile application (see Configuring a mobile application in Adobe Campaign). Here is an example of a code that allows a mobile application to collect any added variables in a notification. In our example, we are using the “VAR” variable.
-
In Android:
code language-none public void onReceive(Context context, Intent intent) { ... String event = intent.getStringExtra("VAR"); ... }
-
In iOS:
code language-none - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { .... if( launchOptions ) { // When application is not already launched, the notification data if any are stored in the key 'UIApplicationLaunchOptionsRemoteNotificationKey' NSDictionary *localLaunchOptions = [launchOptions objectForKey:@"UIApplicationLaunchOptionsRemoteNotificationKey"]; if( localLaunchOptions ) { ... [localLaunchOptions objectForKey:@"VAR"]; ... } } } // Callback called when the application is already launched (whether the application is running foreground or background) - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)launchOptions { if( launchOptions ) { ... [launchOptions objectForKey:@"VAR"]; } }
note caution |
---|
CAUTION |
Adobe recommends choosing short variable names because notification size is limited to 4kB for iOS and Android. |
For iOS
The media has to be downloaded at the notification service extension level.
code language-none |
---|
|
For iOS
At this level, you need to:
-
Associate your content extension to the category sent by Adobe Campaign:
If you want your mobile application to display an image, you can set the category value to “image” in Adobe Campaign and in your mobile application, you create a notification extension with the UNNotificationExtensionCategory parameter set to “image”. When the push notification is received on the device, the extension is called according to the defined category value.
-
Define your notification layout
You need to define a layout with the relevant widgets. For an image, the widget is named UIImageView.
-
Display your media
You need to add code to feed the media data to the widget. Here is an example of code for an image:
code language-none #import "NotificationViewController.h" #import <UserNotifications/UserNotifications.h> #import <UserNotificationsUI/UserNotificationsUI.h> @interface NotificationViewController () <UNNotificationContentExtension> @property (strong, nonatomic) IBOutlet UIImageView *imageView; @property (strong, nonatomic) IBOutlet UILabel *notifContent; @property (strong, nonatomic) IBOutlet UILabel *label; @end @implementation NotificationViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any required interface initialization here. } - (void)didReceiveNotification:(UNNotification *)notification { self.label.text = notification.request.content.title; self.notifContent.text = notification.request.content.body; UNNotificationAttachment *attachment = [notification.request.content.attachments objectAtIndex:0]; if ([attachment.URL startAccessingSecurityScopedResource]) { NSData * imageData = [[NSData alloc] initWithContentsOfURL:attachment.URL]; self.imageView.image =[UIImage imageWithData: imageData]; [attachment.URL stopAccessingSecurityScopedResource]; } } @end