Hybrid Messaging

Introduction

With Hybrid Messaging you are able to deliver messages to any phone number via SMS or Push. When the end users phone number has your app or our free Notifire app installed, the platform can deliver your message via push which is 50% of the SMS price. If the end user on the phone number does not have your app or Notifire installed, the message will automatically be send as an SMS.

Sending a Hybrid Message HTTP

HTTP POST
Endpoint URL: https://sgw01.cm.nl/gateway.ashx
Full SMS Gateway docs can be found here.

Prerequisites:

  • Make sure implement the Hybrid SDK in your app
  • Create the app in the App manager, select the messaging type, upload the Apple certificates and/or Android keys in the App manager.
  • The App manager will give you an App Key and App Secret, the App Key is needed in the request to the gateway.

<?xml version="1.0"?>
<MESSAGES>
  <AUTHENTICATION>
    <PRODUCTTOKEN>00000000-0000-0000-0000-000000000000</PRODUCTTOKEN>
  </AUTHENTICATION>
  <MSG>
    <FROM>Company</FROM>
    <TO>00447911123456</TO>
    <APPKEY>00000000-0000-0000-0000-000000000000</APPKEY>
    <BODY>This message will be delivered as a push message.</BODY>
  </MSG>
</MESSAGES>

Allowed Channel

Allowed channels

The allowed channels field forces a message to only use certain routes. In this field you can define a list of which channels you want your message to use. Not defining any channels will be interpreted als allowing all channels.

The channels available are:

  • SMS
  • Push

The example request below has the allowed channel "Push" and does not contain "SMS". This means the message will be restricted to only taking "Push" routes.

XML Post:

<?xml version="1.0"?>
<MESSAGES>
  <AUTHENTICATION>
    <PRODUCTTOKEN>00000000-0000-0000-0000-000000000000</PRODUCTTOKEN>
  </AUTHENTICATION>
  <MSG>
    <FROM>Company</FROM>
    <TO>00447911123456</TO>
    <APPKEY>00000000-0000-0000-0000-000000000000</APPKEY>
    <ALLOWEDCHANNELS>Push</ALLOWEDCHANNELS>  
    <BODY>This message will be delivered as a push message.</BODY>
  </MSG>
</MESSAGES>

JSON Post:

{
    "messages": {
        "authentication": {
            "producttoken": "00000000-0000-0000-0000-000000000000"
        },
        "msg": [ {
                "from": "SenderName",
                "to": [{
                    "number": "00447911123456"
                }],
                "body": {
                    "content": "This message will be delivered as a push message"
                },
                "allowedChannels": ["Push"],
                "appKey": "00000000-0000-0000-0000-000000000000"
            }
        ]
    }
}

APP Message Type

Hybrid Messaging now allows you use different fallback-to-SMS behaviour in the same App. The HTTP Gateway now supports the appMessageType to set a specific messaging type per message.

By default Hybrid Messaging is using the messaging type you define in the APP Manager

However in the API you can now overrule the "default" messaging type per message. You can select one of the following options:

Default:

For bulk, marketing etc. Messages have unlimited lifetime (as long as the telecom or push service provides). If the user has the App installed we’ll deliver the message as a push notification even if the user is offline. The system will fall back on SMS to send the message only if the push message cannot be delivered (delivery status: failed i.e. no app installed or phone number not verified in the App).

SMS Fall Back: limited fallback behavior

OTP:

One Time Passwords, message lifetime is 30 seconds. If the push does not arrive and you manually decide to send an SMS to your end-user instead, the push message will be terminated to avoid double messages (this is a parameter you can define in the API).

SMS Fall Back: Manual fallback

Critical:

Use this in case the messages are of extreme high priority. It guarantees delivery of your message with a SMS fall back when a push message is sent to a device and it does not respond to the push notification. i.e. app is closed or there is no internet connection.

SMS Fall Back: Automatic fallback if no interaction with the push message

Automatic fall back on SMS after 30 seconds to ensure the message is being delivered. This might result in double messages. (receiving both SMS and Push notification)

XML Post:

<?xml version="1.0"?>
<MESSAGES>
   <AUTHENTICATION>
     <PRODUCTTOKEN>00000000-0000-0000-0000-000000000000</PRODUCTTOKEN>
   </AUTHENTICATION>
   <MSG>
     <FROM>SenderName</FROM>
     <TO>00447911123456</TO>
     <APPKEY>00000000-0000-0000-0000-000000000000</APPKEY>
     <APPMESSAGETYPE>Critical</APPMESSAGETYPE>
     <BODY>This message will use the critical messaging type.</BODY>
   </MSG>
</MESSAGES>

JSON Post:

{
    "messages": {
        "authentication": {
            "producttoken": "00000000-0000-0000-0000-000000000000"
        },
        "msg": [ {
                "from": "Hybrid Test",
                "to": [{
                    "number": "00447911123456"
                }],
                "body": {
                    "content": "This is a critical message"
                },
                "appmessagetype": "critical" ,
                "appKey": "00000000-0000-0000-0000-000000000000"
            }
        ]
    }
}

App Manager

iOS SDK v1.3.0

Overview

Prerequisites

  • Apple iOS development environment (Xcode 6.0+, SDK, etc.)
  • Hybrid Messaging Application Key / Application Secret Key
  • Push enabled provisioning profile
  • Basic knowledge of iOS app development

Package Contents

The iOS SDK contains the following things:

Integration

CocoaPods is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries like HybridMessaging in your projects. See http://cocoapods.org for more information on how to install and use CocoaPods.

Podfile

platform :ios, '7.0'

pod 'HybridMessagingSDK', '~> 1.2'

Installation via Static Library

The HybridMessaging SDK can be added to your application by dragging the HybridMessaging.h and libHybridMessaging.a file into your project in Xcode. Make sure to enable “Copy items if needed” in the “Choose options for adding these files” dialog.

Initialisation

  1. Open the Info.plist and add a new item with the key “Required background modes” and select “App downloads content in response to push notifications”
  2. Open your AppDelegate class and import the HybridMessaging header: #import <HybridMessaging.h>
  3. Add the following lines in the application:didFinishLaunchingWithOptions: method
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [HybridMessaging configureWithKey:@"YOUR_APPLICATION_KEY"
                                    secret:@"YOUR_APPLICATION_SECRET"]; 

    [HybridMessaging enable];
}

Notes: the "enable" method triggers a dialog to the user to request permission to sent push notifications. You may want to call the method at a later time to increase the likelihood that the user will accept the notifications. The permission dialog is only triggered once. When the user has denied the request it can be enabled again from the Settings menu.

Set development mode (optional)

Set the device development mode (defaults to production)

+ (void)setDevelopment:(BOOL)yesOrNo;

When this value is set to YES, the device registration is marked as a development device, and our server automatically uses the development push certificate (if configured) while sending a push notification to the device.

Set this value before calling any device verification methods, preferably at the start of the application in - (BOOL)application:didFinishLaunchingWithOptions:

Note: Remember to set this value to NO in a production build or remove the method call

Basic usage

The central class in the Hybrid Messaging SDK is called HybridMessaging and holds references to methods for notifications and authentication functionality. All methods calls are static.

The HybridMessaging class also contains a method for the Device ID and Push Token:

+ (NSString *)deviceId;
+ (NSString *)pushToken;
+ (NSString *)msisdn;
+ (NSString *)version;

Phone number verification

Methods

+ (void)requestPhoneVerification:(NSString *)msisdn 
                         handler:(void (^)(HMDeviceRegistrationStatus status))statusHandler;

+ (void)requestPhoneVerificationVoiceCall:(NSString *)msisdn 
                         handler:(void (^)(BOOL success))statusHandler;

+ (void)requestPinVerification:(NSString *)pin 
                        handler:(void (^)(HMDeviceRegistrationStatus status))statusHandler;

+ (void)requestVerificationStatus:(void(^)(HMDeviceRegistrationStatus status))statusHandler;

The device registration status can have one of the following values:

- HMDeviceRegistrationStatusUnknown
- HMDeviceRegistrationStatusUnverified
- HMDeviceRegistrationStatusWaitingForPin
- HMDeviceRegistrationStatusLastPinVerificationFailed
- HMDeviceRegistrationStatusPinVerified
- HMDeviceRegistrationStatusInvalid

Notes: Phone number is in MSISDN format and must start with the country code: e.g. 0031 for the Netherlands.

Example

[HybridMessaging requestPhoneVerification:phoneNumberTextField.text 
                                  handler:^(HMDeviceRegistrationStatus status) {
    if (status == HMDeviceRegistrationStatusWaitingForPin
        || status == HMDeviceRegistrationStatusLastPinVerificationFailed) {
        // Show PIN view
    } else {
        // Handle error
    }
}];

Push notifications

Methods

Set the user notification types (optional). Defaults to Alert, Badge and Sound.

+ (void)setUserNotificationTypes:(HMUserNotificationType)types;

Get the current notification types

+ (HMUserNotificationType)userNotificationTypes;

All available notification types:

- HMUserNotificationTypeNone // the application may not present any UI upon a notification being received
- HMUserNotificationTypeBadge // the application may badge its icon upon a notification being received
- HMUserNotificationTypeSound // the application may play a sound upon a notification being received
- HMUserNotificationTypeAlert // the application may display an alert upon a notification being received

Notes: Make sure to set the notification types before calling the +enable method.

Example

[HybridMessaging setUserNotificationTypes:(HMUserNotificationTypeBadge | 
                                           HMUserNotificationTypeAlert | 
                                           HMUserNotificationTypeSound)];

Enable push notification.

+ (void)enable;

Disable push notification.

+ (void)disable;

Push notification blocks

You may set the following blocks in your application to react to the push notification events.

[HybridMessaging setDidReceiveRemoteNotificationBlock:^(NSDictionary *userInfo) { 
    NSLog(@"%@", userInfo);
}];

Called when a push notification is received

[HybridMessaging setDidRegisterForRemoteNotificationsWithDeviceTokenBlock:^(NSData *deviceToken) { 
    NSLog(@"%@", [deviceToken description]);
}];

Called when a push token is received

[HybridMessaging setDidFailToRegisterForRemoteNotificationsWithErrorBlock:^(NSError *error) { 
    NSLog(@"%@", error);
}];

Called when the push registration fails

Messages

Methods to receive the messages that have been sent by the Hybrid Messaging.

+ (void)messages:(void (^)(NSArray *messages, NSError *error))handler;
+ (void)messagesWithLimit:(NSInteger)limit handler:(void (^)(NSArray *messages, NSError *error))handler;
+ (void)messagesWithLimit:(NSInteger)limit offset:(NSInteger)offset handler:(void (^)(NSArray *messages, NSError *error))handler;

Example

Get the last 100 messages

[HybridMessaging messagesWithLimit:100 handler:^(NSArray *messages, NSError *error) { 
    if (error) {
        // Handle error
    } else {
        for (NSDictionary *message in messages) {
            NSString *id = message[@"ID"];
            NSString *body = message[@"Body"]; 
            NSString *dateTime = message[@"DateTime"]; 
            NSString *sender = message[@"Sender"];

            // Do something with message
        }         
    }
}];

GeoIP

Get geolocation information about the app user.

+ (void)requestGeoIpInfo:(void(^)(NSDictionary *info))handler;

Carrier update notification

Sets a callback to be executed when the carrier has changed.

+ (void)setDeviceDidUpdateCarrierBlock:(void(^)(CTCarrier *carrier))block;

Android SDK v2.1.0

Setup

Prerequisites

  • GCM Sender ID (can be retrieved from the Google Developers Console)
  • Hybrid Messaging Application Key / Application Secret Key

Configuration

The library is hosted in the Maven repository Bintray. All required changes (permissions, services and receivers) will automatically be merged as they're included in the Android Archive (AAR).

Add the dependency to your build.gradle:

dependencies {
    compile 'com.cm.hybridmessagingsdk:hybridmessagingsdk:2.1.0'
}
Usage

The heart of the SDK is the HybridMessaging class. It contains all the functionality (like registering, messaging and notification settings) and is the only class needed to communicate with the SDK.

  1. Initialize the SDK
  2. Check Google Play Services availability
  3. Register the device
  4. ...

Initialization is required before calling any other method in HybridMessaging

Initialization

Initialize the SDK

Initialize the Hybrid Messaging SDK. Initialization is required before calling any other method in HybridMessaging. The initialization process should happen as early as possible, initializing in the Application class ensures the SDK is always initialized.

There's two ways to initialize the Hybrid Messaging SDK:

  1. By declaring the keys in metadata
  2. By passing the keys programmatically

Using metadata

Add this metadata to the <application /> tag in your Android Manifest:

<meta-data
    android:name="com.cm.hybridmessaging.api_key"
    android:value="your_hybridmessaging_key" />
<meta-data
    android:name="com.cm.hybridmessaging.secret"
    android:value="your_hybridmessaging_secret" />
<meta-data
    android:name="com.cm.hybridmessaging.projectid"
    android:value="@string/project_id" />

Add the GCM Sender ID to your strings.xml:

<string name="project_id">your_gcm_sender_id</string>

And call the initialize method:

HybridMessaging.initialize(Context context)

The keys will be read from the Android Manifest, so there's no need to provide them programmatically.

Example
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // initialize the Hybrid Messaging SDK
        HybridMessaging.initialize(this);
    }
}

Declare your Application class in the <application /> tag in the Android Manifest:

<application [icon, label, theme, etc.] android:name=".MyApplication" />

Programmatically

Provide the keys programmatically, do this if you can't or prefer not to use metadata.

HybridMessaging.initialize(Context context, String apiKey, String apiSecret, String senderId)
Example
public class MyApplication extends Application {
    private static final String API_KEY = "your_hybridmessaging_key";
    private static final String API_SECRET = "your_hybridmessaging_secret";
    private static final String SENDER_ID = "your_gcm_sender_id";

    @Override
    public void onCreate() {
        super.onCreate();
        // initialize the Hybrid Messaging SDK
        HybridMessaging.initialize(this, API_KEY, API_SECRET, SENDER_ID);
    }
}

Declare your Application class in the <application /> tag in the Android Manifest:

<application [icon, label, theme, etc.] android:name=".MyApplication" />

Check Google Play Services

Google Play Services is required to receive push notifications. This is required by the Hybrid Messaging SDK in order to function. It's recommended to check if it's available through Google's API, so the user's guaranteed to be able to receive push notifications. Check this preferably in the startup activity.

Example

Check if the Google Play Services is available. If it's not, a dialog with a solution will be shown if possible.

private boolean checkPlayServices() {
    GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
    int resultCode = apiAvailability.isGooglePlayServicesAvailable(this);
    if (resultCode != ConnectionResult.SUCCESS) {
        if (apiAvailability.isUserResolvableError(resultCode)) {
            apiAvailability.getErrorDialog(this, resultCode, REQUEST_GOOGLE_PLAY_SERVICES).show();
        } else {
            Log.w(TAG, "This device does not support the required Google Play Services.");
            // show the user an error message
        }
        return false;
    }
    return true;
}

And handle the result from the error dialog.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case REQUEST_GOOGLE_PLAY_SERVICES:
            // if Play Services is available now, re-init the SDK and continue
            HybridMessaging.initialize(getApplicationContext());
        default:
            super.onActivityResult(requestCode, resultCode, data);
    }
}

REQUEST_GOOGLE_PLAY_SERVICES is the request code used to indentify the request in the onActivityResult(...) callback. Its value may be any number, but should be unique: no other requests should use the same value. For example:

private static final int REQUEST_GOOGLE_PLAY_SERVICES = 1;
Registration

Normal registration flow:

  1. Check the current verification status
  2. Register the user using a phone number
  3. The user receives a text containing a verification PIN
  4. Verify the user using the verification PIN

Phone numbers are in MSISDN format and have to start with the country code. Example: the country code for the Netherlands is 0031, the MSISDN will look like 0031612345678.

Check verification status

Get the status of the verification/registration of the user.

HybridMessaging.getVerificationStatus(OnVerificationStatus listener)

Listener

OnVerificationStatus interface:

void onVerificationStatus(VerificationStatus status);
void onError(Throwable throwable);

The enum VerificationStatus has four values:

VerificationStatus Meaning
Unverified The user is not registered
Verified The user is registered and the phone number is verified
LastPinVerificationFailed The user is registered, but the last provided pin was invalid
WaitingForPin The user is registered, but hasn't provided a pin

Example

HybridMessaging.getVerificationStatus(new OnVerificationStatus() {
    @Override
    public void onVerificationStatus(VerificationStatus status) {
        switch (status) {
            case Unverified:
                // show registration screen
                break;
            case Verified:
                // already registered
                break;
            case LastPinVerificationFailed:
            case WaitingForPin:
                // show pin input
                break;
        }
    }

    @Override
    public void onError(Throwable throwable) {
        Log.e("MainActivity", "Error getting verification status", throwable);
    }
});

Register new user

Register a new user.

HybridMessaging.registerNewUser(String msisdn, OnRegistrationListener listener)

The user will receive an SMS message containing a validation PIN.

Listener

OnRegistrationListener interface:

void onReceivedRegistration(Registration registration);
void onError(Throwable throwable);

Example

HybridMessaging.registerNewUser(msisdn, new OnRegistrationListener() {
    @Override
    public void onReceivedRegistration(Registration registration) {
        // ask user to enter verification PIN
    }

    @Override
    public void onError(Throwable throwable) {
        Log.e("MainActivity", "Error registering new user", throwable);
    }
});

Verify PIN

Confirm the MSISDN by providing the verification PIN sent to the phone number when the user was registered.

HybridMessaging.registerUserByPincode(String verificationCode, OnVerificationStatus listener)

Listener

See Check verification status for information about this listener.

Example

HybridMessaging.registerUserByPincode(verificationCode, new OnVerificationStatus() {
    @Override
    public void onVerificationStatus(VerificationStatus status) {
        switch (status) {
            case Verified:
                // verification was successful
                break;
            case LastPinVerificationFailed:
                // provided PIN was invalid
                break;
        }
    }

    @Override
    public void onError(Throwable throwable) {
        Log.e("MainActivity", "Error registering user by PIN", throwable);
    }
});

Request new PIN

When the user wants the PIN re-sent.

HybridMessaging.requestNewVerificationPin(String msisdn, OnRegistrationListener listener)

Listener

See Register new user for information about this listener.

Example

HybridMessaging.requestNewVerificationPin(msisdn, new OnRegistrationListener() {
    @Override
    public void onReceivedRegistration(Registration registration) {
        // ask user to enter verification pin
    }

    @Override
    public void onError(Throwable throwable) {
        Log.e("MainActivity", "Error sending new PIN", throwable);
    }
});

Request PIN over voice call

If the user prefers the PIN to be provided over a voice call, the user will be called in the language the device is set to.

HybridMessaging.doVoiceCall(String msisdn, VoiceCallListener listener)

listener is optional

Listener

VoiceCallListener interface:

void onSuccess();
void onError(Throwable throwable);

Example

HybridMessaging.doVoiceCall(msisdn, new VoiceCallListener() {
    @Override
    public void onSuccess() {
        // user is being called
    }

    @Override
    public void onError(Throwable throwable) {
        Log.e("MainActivity", "Error requesting PIN over voice call", throwable);
    }
});

Check registration

Check if the user is registered.

HybridMessaging.hasRegistration() : boolean

Note: this does not replace checking the verification status, as this methods only checks the registration locally.

Reset registration

Remove the registration, so a new user could be registered.

HybridMessaging.resetLogin()

Get MSISDN

The method reads the MSISDN from the SIM card if it's available, will read it from the registration otherwise.

HybridMessaging.getMsisdn() : String

Note: in order to read the MSISDN from the SIM card, the permission READ_PHONE_STATE is required. Without this permission, the MSISDN will only be read form the current registration.

Get Device ID

Get the Device ID of the registration.

HybridMessaging.getDeviceId() : String
Messages

Get messages

All messages

Get all messages.

HybridMessaging.getMessages(OnMessageListener listener)

Messages with limit

Get a certain amount of messages.

HybridMessaging.getMessages(int amount, OnMessageListener listener)

Messages with limit and offset

skip a certain number and get a certain amount of messages.

HybridMessaging.getMessages(int skip, int amount, OnMessageListener listener)

Messages after a certain date

Get all messages after a given date.

HybridMessaging.getMessagesFromDate(long dateInMillisec, OnMessageListener listener)

dateInMillisec: date formatted as a UNIX timestamp in milliseconds

Filtered messages

Get messages filtered by a custom filter.

HybridMessaging.getMessagesWithFilter(Filter filter, OnMessageListener listener)

You can add one or more options to the Filter. The filter works with OData. Filter options: OPTION_EXPAND, OPTION_FILTER, OPTION_ORDER_BY, OPTION_SELECT, OPTION_SKIP and OPTION_TOP.

Filter example
Filter filter = new Filter();
filter.addFilter(Filter.OPTION_SELECT, "ID,Body,DateTime,Sender,UpdateIDs");
filter.addFilter(Filter.OPTION_SKIP, "10");
filter.addFilter(Filter.OPTION_TOP, "5");
filter.addFilter(Filter.OPTION_FILTER, "DateTime gt DateTime'2016-01-01T12:00:00.000");

Listener

OnMessageListener interface:

void onReceivedMessages(int statusCode, Headers headers, Message[] messages);
void onError(Throwable throwable);

Example

int skip = 10;
int amount = 5;
HybridMessaging.getMessages(skip, amount, new OnMessageListener() {
    @Override
    public void onReceivedMessages(int statusCode, Headers headers, Message[] messages) {
        // show messages
    }

    @Override
    public void onError(Throwable throwable) {
        Log.e("MainActivity", "Error getting messages", throwable);
    }
});

Mark as read

Mark read at current date

Mark a message as read by the recipient at the current date.

HybridMessaging.markMessageAsRead(Message message, MarkAsReadListener listener)

listener is optional

Mark read at specified date

Mark a message as read by the recipient at the specified date.

HybridMessaging.markMessageAsRead(Message message, Date date, MarkAsReadListener listener)

listener is optional

Listener

MarkAsReadListener interface:

void onSuccess();
void onError(Throwable throwable);

Example

HybridMessaging.markMessageAsRead(message, new MarkAsReadListener() {
    @Override
    public void onSuccess() {
        // message is successfully marked as read
    }

    @Override
    public void onError(Throwable throwable) {
        Log.e("MainActivity", "Error marking message as read", throwable);
    }
});
Notifications

The SDK automatically shows notifications in the status bar with the default settings.

Default title: The application name Default icon: The application icon Both are retrieved from the <application /> tag in the AndroidManifest.xml

Set title

Set a custom title for default notifications.

title: pass null to reset it to its default (application name).

HybridMessaging.setNotificationTitle(String title)

Set icon

Set a custom icon for default notifications.

HybridMessaging.setNotificationIcon(int resourceId)

resourceId: a drawable resource ID, pass 0 to reset it to its default (application icon).

Example

HybridMessaging.setNotificationIcon(R.drawable.ic_notification);

Default notifications

Change the behaviour of the SDK showing notifications. By default, the SDK shows notifications when receiving a message.

HybridMessaging.fireNotificationsByDefault(boolean showNotifications)

This is best to be used in conjunction with notification listener and show custom notifications.

Receive notifications

If you want to be notified when a new Notification is received, you could register a listener for this. For example, when you want to show custom notifications.

HybridMessaging.setHybridNotificationListener(HybridNotificationListener listener)

Note: if you want to show custom notifications, default notifications should be disabled

Listener

HybridNotificationListener interface:

void onReceiveHybridNotification(Context context, Notification notification);

Example

NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(context)
        .setContentTitle("Hybrid Messaging SDK Example")
        .setSmallIcon(R.drawable.ic_notification)
        .setContentText(notification.getMessage())
        .setAutoCancel(true);

// gets an instance of the NotificationManager service
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notificationId, notifBuilder.build());

Detect disabled notifications

Users have the ability to disable notifications for apps, the SDK can detect if the user has disabled notifications for this app. When notifications are disabled, SMS messages will be sent instead of push messages, so the user will still receive Hybrid messages.

Enable or disable automatic detection.

HybridMessaging.setDetectDisabledNotifications(boolean enable)

Get if automatic detection is enabled.

HybridMessaging.getDetectDisabledNotifications() : boolean

By default, automatic detection is enabled.

SIM card changes

If a device has a different SIM card, chances are the phone number has changed. As Hybrid Messaging is about phone numbers, it's a good idea to check if a user's phone number has changed. The Hybrid Messaging SDK is able to detect a replaced SIM card and could notify you. When a SIM card change is detected, it's recommended to reset the registration and let the user verify its phone number again.

HybridMessaging.setOnSimChangedListener(OnSimcardChangedListener listener)

Note: to be able to detect SIM card changes, the permission READ_PHONE_STATE is required.

Add this to the <manifest /> tag of your Android Manifest:

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

Listener

Interface OnSimcardChangedListener:

void onSimcardChanged(boolean isChanged, String simState);

Lifecycle

In any Activity override the Activity.onResume() and Activity.onPause() and call them on the SDK. This will make it start and stop listening for changes to respect the Activity lifecycle.

HybridMessaging.onResume()
HybridMessaging.onPause()

Recommended: call onResume() and onPause() in every Activity shown after the user is registered.

Example

HybridMessaging.setOnSimChangedListener(new OnSimcardChangedListener() {
    @Override
    public void onSimcardChanged(boolean isChanged, String simState) {
        if (isChanged) {
            // reset the registration
        }
    }
});
public class MainActivity extends Activity {

    @Override
    protected void onResume() {
        super.onResume();
        HybridMessaging.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        HybridMessaging.onPause();
    }
}

Manual check

It's also possible to check for SIM card changes manually.

HybridMessaging.didSimStateChange() : boolean
Miscellaneous

Get SDK version

Get the current version of the Hybrid Messaging SDK.

HybridMessaging.getSDKVersion() : String

Customize user agent

To customize the user agent sent to the Hybrid Messaging servers with every call, obtain the UserAgent instance. It's possible to change the app name, app version and language. By default, these properties will be read from the app.

HybridMessaging.getUserAgent() : UserAgent
Upgrade from 1.x
  1. Cleanup Android Manifest
  2. Upgrade dependency
  3. Apply code changes
  4. Replace deprecated methods

Cleanup Android Manifest

Since 2.0.0 are all required permissions, receivers and services declared in the library's Android Manifest. All required changes to the Android Manifest will automatically be merged.

More info: Include library

  1. Remove the permissions:

    <!--Necessary for all outgoing internet connections-->
    <uses-permission android:name="android.permission.INTERNET" />
    <!--Necessary for connecting with the play store and retrieving registration id-->
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <!--Keep the service awake during processing incoming notifications-->
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <!--Receive notifications from the CCS/GCM platform of google-->
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    
  2. Remove receivers:

    <receiver android:name="com.cm.hybridmessagingsdk.receivers.GcmBroadcastReceiver"
           android:permission="com.google.android.c2dm.permission.SEND">
     <intent-filter>
         <action android:name="com.google.android.c2dm.intent.RECEIVE" />
         <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
         <category android:name="com.cm.hybridmessaging" />
     </intent-filter>
    </receiver>
    
  3. Remove services:

    <service android:name="com.cm.hybridmessagingsdk.GCMNotificationIntentService" />
    

Upgrade dependency

Upgrade the dependency to version 2.1.0 in your build.gradle:

dependencies {
    compile 'com.cm.hybridmessagingsdk:hybridmessagingsdk:2.1.0'
}

More info: Include library

Code changes

Due to switching from Apache HttpClient to OkHttp, the signature of the onReceivedMessages(...) method in the OnMessageListener has changed.

Previous:

void onReceivedMessages(int statusCode, Header[] headers, Message[] messages);

Now:

void onReceivedMessages(int statusCode, Headers headers, Message[] messages);

More info: Get messages

Deprecated methods

Deprecated method Replacement
HybridMessaging#HybridMessaging(Context) HybridMessaging.initialize(Context)
HybridMessaging.setUserAgent() : UserAgent HybridMessaging.getUserAgent() : UserAgent
Message#getUpdateId() : String Message#getUpdateIds() : String[]
Message#setUpdateId(String) : void Message#setUpdateIds(String[]) : void
Notification#getNotificationId() : String Notification#getId() : int
Changelog

2.1.0

  • Add an initialization method to pass the credentials programmatically.
  • Detect when the user disabled notifications and fall back to SMS messages (configurable)
  • When clicking on a notification, the launcher activity of the app will be started

2.0.0

  • Internal rewrite, highlights:
    • Move from Apache HttpClient (deprecated) to OkHttp.
    • Replace the (deprecated) way of registering at and receiving push messages from GCM.
    • Automatically retry sending the GCM registration token in case of a failure (using exponential back-off).
  • Support for providing PINs via voice calls.
  • Support for marking messages as read.
  • All required permissions, services and receivers are declared in the library's Android Manifest. It's not needed anymore to manually include them.
  • Allow re-initialization of the SDK.
  • Deprecation of several methods.

Cordova Plugin v1.0.0

Setup

Supported Platforms

  • Android
  • iOS

Prerequisites

  • For each target native platform, the minimum system requirements of the corresponding native SDK apply (see Android SDK and iOS SDK prerequisites)
  • Cordova 6.x or higher

Installation

  • execute within the target Cordova project:

    cordova plugin add https://github.com/CMTelecom/cm-plugin-hybridmessaging.git#1.0.0
    

    OR

  • add:
    <plugin name="cm-plugin-hybridmessaging" spec="https://github.com/CMTelecom/cm-plugin-hybridmessaging.git#1.0.0" />
    
    to the config.xml file of the project.
Initialization

The plugin intialization consists of two steps, namely:

Service configuration

Service configuration is performed by the means of the hybridmessaging.configureService(config) call. This configures the hybrid messaging service with API key, API secret, Android sender ID , a callback for handling incoming push notification content and finally, a callback for handling SIM card/carrier changes.

Parameters

  • config: Object, (required) - config object with format:
    {
      appKey : 'string',
      appSecret : 'string',
      senderId : 'string', (required on Android only)
      notificationCallback : 'function',
      deviceCarrierUpdateCallback : 'function'
    }
    

Example

var config = {
    appKey : YOUR_APP_KEY,
    appSecret : YOUR_APP_SECRET,
    senderId : YOUR_ANDROID_SENDER_ID,
    notificationCallback: notificationCallback,
    deviceCarrierUpdateCallback: deviceCarrierUpdateCallback
};
window.hybridmessaging.configureService(config);
function notificationCallback(pushPayload) {
    console.log(pushPayload);

    #push payload is raw platform-specific payload including keys such as 'alert' on iOS and 'message' on Android.
    var message = pushPayload.alert ? pushPayload.alert : pushPayload.message;

    $cordovaDialogs.alert(message, 'Push message', 'OK');
}
function deviceCarrierUpdateCallback() {
    console.log('Device carrier has changed');
}

IMPORTANT: the configureService method should be called as early in the application's life cycle as possible and prior to executing any other call related to hybrid messaging. All hybrid messaging methods called prior to service configuration will return with an error.

Service starting

The hybrid messaging service is started by calling hybridmessaging.startMessagingService(successCallback, errorCallback). This prepares the client for receiving hybrid messages.

Parameters

  • successCallback: function, (required) - callback function to be called upon successfully started messaging service

  • failureCallback: function, (optional) - callback function to be called upon error

Example

window.hybridmessaging.startMessagingService(function() {
    console.log('Messaging service has started successfully');
}, function(error) {
    console.log('Failed to start messaging service: ' + error);
});

IMPORTANT: the startMessagingService method should be called after executing the configureService method and should complete with success prior to executing any other call related to hybrid messaging. All hybrid messaging methods called prior to successful service starting (except for configureService which should be called before it and as early in the application life cycle as possible) will return with an error.

Phone number verification

Verification request

In order for the user to be able to receive hybrid notifications, the user's phone number (MSISDN) needs to be confirmed at least once in the life time of the mobile application. This is performed by requesting verification via the requestVerification(msisdn, successCallback, errorCallback) which triggers an SMS with a one-time PIN to be sent to the user's device:

Parameters

  • msisdn: string, (required) - phone number to verify

  • successCallback: function, (required) - callback function to be called upon successfully requesting number verification.

  • failureCallback: function, (optional) - callback function to be called upon error

Example

var msisdn = '0031612345678';
window.hybridmessaging.requestVerification(msisdn, function(status) {
    console.log(status);
}, function(error) {
     console.log('Phone number verification failed: ' + error);
});

PIN verification

Once successfully received on the user's device, the one-time PIN code should be passed back to the hybrid messaging plugin for verification. This is performed using the verifyPin(pin, successCallback, errorCallback) call.

Parameters

  • pin: string, (required) - pin to verify

  • successCallback: function, (required) - callback function to be called upon successfully executing PIN verification (with verification either passed or failed, but in any case correctly executed)

  • errorCallback: function, (optional) - callback function to be called upon error

Example

var pin = '0785';
window.hybridmessaging.verifyPin(pin, function(status) {
    console.log(status);
}, function(error) {
   console.log('Failed to verify pin: ' + error);
});

Passing PIN via a voice call

In case if the default SMS-based number verification failed, the user can request the one time PIN code to be passed to them using an alternative method, namely via requestVerificationVoiceCall(successCallback, errorCallback) . The requested voice call, if successful, will provide the same PIN code as was originally sent via SMS for verification.

Parameters

  • successCallback: function, (required) - callback function to be called upon successfully requesting PIN voice call

  • errorCallback: function, (optional) - callback function to be called upon error

Example

window.hybridmessaging.requestVerificationVoiceCall(function() {
  console.log('Successfully performed a voice call');
}, function(error) {
  console.log('Failed to perform a voice call: ' + error);
});

IMPORTANT: Due to its fallback nature the requestVerificationVoiceCall method should only be called after regular SMS-based verification has been requested using the requestVerification method. Calling requestVerificationVoiceCall before attempting at least one requestVerification call in the application's lifetime will result in an error.

Checking the verification status

Each of the methods used in phone number verification returns upon successful execution (so through its respective successCallback) a status code which represents one of the discrete statuses from the DeviceRegistrationStatus enumeration:

DeviceRegistrationStatus.UNVERIFIED,
DeviceRegistrationStatus.WAITING_FOR_PIN,
DeviceRegistrationStatus.LAST_PIN_VERIFICATION_FAILED,
DeviceRegistrationStatus.PIN_VERIFIED,
DeviceRegistrationStatus.INVALID,
DeviceRegistrationStatus.UNKNOWN

Additionally, at any point in the application flow, independently of the method calls decribed above, it is possible to explicitly call the getVerificationStatus(successCallback, errorCallback) method which upon success will also deliver the current status of the verification.

Parameters

  • successCallback: function, (required) - callback function to be called upon successfully fetching the current status

  • errorCallback: function, (optional) - callback function to be called upon error

Example

window.hybridmessaging.getVerificationStatus(function(status) {
    console.log(status);
}, function(error) {
    console.log('Failed to get verification status: ' + error);
});
Messages

Getting the messages

In order to fetch all previously received hybrid messages, the getMessages(limit, offset, successCallback, errorCallback) can be used.

Parameters

  • limit: number, (optional) - amount of messages to retrieve

  • offset: number, (optional) - offset from which messages to retrieve

  • successCallback: function, (required) - callback to be called upon successful message retrieval

  • errorCallback: function, (optional) - callback function to be called upon error

Example

var limit = 5;
var offset = 10;
window.hybridmessaging.getMessages(limit, offset, function(messages) {
    console.log(messages);
}, function(error) {
    console.log('Failed to retrieve messages: ' + error);
});
Auxiliary methods

Device ID

It is possible to programmatically obtain the device ID value which is used by the hybrid messaging system in order to uniquely identify a device of a user. For this, the getDeviceIdValue(successCallback, errorCallback)call can be used.

Parameters

  • successCallback: function, (required) - callback function to be called upon successfully fetching device ID

  • errorCallback: function, (optional) - callback function to be called upon error

Example

window.hybridmessaging.getDeviceIdValue(function(deviceId) {
    console.log('Device id is ' + deviceId);
}, function(error) {
    console.log('Failed to fetch device id: ' + error);
});

MSISDN

It is possible to programmatically obtain the device's phone (MSISDN) number as long as only it has ever been registered with the hybrid messaging system during the mobile application's life time. For this, the getMsisdnValue(successCallback, errorCallback)call can be used.

Parameters

  • successCallback: function, (required) - callback function to be called upon successfully fetching theMSISDN

  • errorCallback: function, (optional) - callback function to be called upon error

Example

window.hybridmessaging.getMsisdnValue(function(msisdn) {
    console.log('MSISDN is ' + msisdn);
}, function(error) {
     console.log('Failed to fetch MSISDN: ' + error);
});

Development mode (iOS only)

In case when it's desired to use the Apple Push Notification Service (APNS) development sandbox instead of the actual APNS production system, a setDevelopment(enableDevelopment) call should be executed client side in order to notify the hybrid messaging server about the need for using an appropriate APNS endpoint.

Parameters

  • enableDevelopment: boolean, (required)

Example

window.hybridmessaging.setDevelopment(true);

Hybrid Messaging API

Introduction

Apps that register with and retrieve messages from CM’s Hybrid Messaging platform use a REST API to communicate. This document is a guide on how to use the API. There are some prerequisites (like authorization) for every request. These are explained in chapter 2.

In chapter 3 we explain how to register and chapter 4 is all about receiving messages. As this API is a RESTful interface, we use HTTP verbs to define an action:

  • GET - Retrieve something
  • POST - Create something new
  • PUT - Update something existing
  • DELETE - Delete something

More information on REST: http://en.wikipedia.org/wiki/Representational\_state\_transfer#Applied\_to\_web\_services

The data format is JSON. More information on JSON: http://www.json.org

  • Android Gradle Package
  • iOS CocoaPod
Request requirements

Authentication

The Authorization header is used to verify requests from your app are originating from the app. The app 'signs' the request with a calculated OAuth 2.0 Message Authentication Code (MAC) signature and the REST API will check this by calculating the same signature itself and comparing this calculated signature with the mac value in the Authorization header.

The Authorization header needs three values:

  • The appKey identifies your app. You get the appKey from us.
  • ts is the timestamp of the request.
  • mac is the message authentication code, the signature of the request. It is the hash of the request signed with the appSecret. You get the appSecret from us.

The value of the Authorization header will look like this:

MAC kid="8eda08f7-5249-40c2-9ef7-ce573f3a8dca" ts="1464242891" mac="dkViwAV9Pi5xiab39VRUq+gU4Co="

The message authentication code (mac) is calculated by using hmac-sha-1 on a string representing the request. The string representing the request looks like this:

POST /v1.0/deviceregistrations HTTP/1.1
api.cmtelecom.com
1464242891

The query string, if used, should also be included:

POST /v1.0/messages?$top=100 HTTP/1.1
api.cmtelecom.com
1464242891

If authorization fails, the server will respond with HTTP Status 401 Unauthorized.

User Agent

Your app also needs to include a User-Agent header. This contains information about the app, the hybrid messaging library used and the device. It should at least contain the mobile platform (iOS, Android or WindowsPhone). Other information is optional. It is useful to include the operating system, app name, user name, device brand name, device model name, user language and/or the app version.

Android example:

MyApp/1.0 (Android 6.0.1; LGE; Nexus 5X; en_US)

If the UserAgent is not correct or missing, the server responds with HTTP Code 400 Bad Request.

DeviceID

To uniquely identify the app install on the device, the Hybrid Messaging platform generates a globally unique identifier (GUID). After you’ve retrieve a DeviceID as reply on the first device registration request, you need to include a DeviceID header on every request with this GUID as its value. It’s mandatory for every request, except when creating a new device registration.

If the DeviceID is not correct, the server responds with HTTP Code 400 Bad Request.

Do not mistake the DeviceID with the deviceID from your phone.

Date and time format

The API uses the ISO 8601 Date/Time format, with a T between the date and the time. In short, this is the format:

YYYY-MM-DDTHH:mm:ss.ms

Do not use local time, but always use UTC time.

Example

A full HTTP request will look like this:

PUT /v1.0/deviceregistrations HTTP/1.1
Host: api.cmtelecom.com
Authorization: MAC kid="8eda08f7-5249-40c2-9ef7-ce573f3a8dca" ts="1464242891" mac="dkViwAV9Pi5xiab39VRUq+gU4Co="
Content-Type: application/json
User-Agent: MyApp/1.0 (Android 6.0.1; LGE; Nexus 5X; en_US)
DeviceID: 0c6bf13c-8d1c-474c-ab66-74dadb1c3d7d
{
    "Msisdn": "0031600000000"
}
Device Registrations

A "device registration" in the context of this API is an install of an app on a device.

Get device registration information

To get information about the apps device registration, simply send a GET request to /v1.0/deviceregistrations and you get the following response:

{
    "DeviceID":"9386b95d-d31b-49bd-8bf8-f203bed9236e", 
    "Msisdn":"0031600000000", 
    "Platform":"Android", 
    "RegistrationStatus":"Unverified", 
    "Created":"2016-05-22T18:02:45.427", 
    "RegisteredForPushNotification":false, 
    "AppKey":"73838e3f-186c-433a-a8e1-e73c88856abc", 
    "AppName":"MyApp",
    "UserAgent":"MyApp/1.0 (Android 6.0.1; LGE; Nexus 5X; en_US)"
}

RegistrationStatus can have the following values:

  • Unverified – registration without MSISDN (phone number)
  • WaitingForPin – verification code has been sent to the MSISDN
  • LastPinVerificationFailed – verification code was incorrectly verified
  • PinVerified – MSISDN is known and verified

When the device registration is not found, the server returns HTTP Code 404 Not found.

New device registration

To register a new device registration, you send a POST request to /v1.0/deviceregistrations. You can even send a request without any field, but you still have to provide an empty object:

{}

You can also choose to include the push token and/or MSISDN already:

{
    "PushToken":"3b19403cc169950f61471bec7fb21722e9a26f5e35c70a"
}

PushToken is the token that the app retrieves from a push notification cloud service like Windows Notification Services, Google Cloud Messaging or Apple Push Notification Server.

Update device registration

To update an existing device registration, you send a PUT request to /v1.0/deviceregistrations with any field you want to update. You can leave out any field you don’t want to update.

{
    "PushToken":"3b12be3456ea60b82b71bec7fb21722e9a26f5e35c70a",
    "Msisdn":"0031600000000"
}

Msisdn is the mobile subscriber identification number. A internationally formatted phone number starting with the country prefix with leading zeroes.

Verification process

When a device registration request contains an MSISDN (phone number) which is not verified yet, the platform will automatically send a verification SMS message to that MSISDN.

You need to give your user a way to put in the verification code, after which your app sends a PUT request to /v1.0/deviceregistrations with the appropriate code:

{
    "VerificationCode":"1234"
}

If the code is correct, the server will respond with:

{
    ...
    "RegistrationStatus":"PinVerified", 
    "RegisteredForPushNotification":true,
    ...
}

If the code is not correct, the server responds with:

{
    ...
    "RegistrationStatus":"LastPinVerificationFailed", 
    "RegisteredForPushNotification":false,
    ...
}
Messages

Get messages

To retrieve all messages for the app, send a GET request to /v1.0/messages. The response looks like this:

[
    {
        "ID":"7af65395-2dc9-45c4-aa58-a7f53e33c4f3", 
        "UpdateID":"6509643555", 
        "Recipient":"0031600000000", 
        "Sender":"MyApp",
        "Body":"Your first message!", 
        "DateTime":"2016-05-22T23:41:28.563", 
        "Status":"Delivered"
    },
    {
        "ID":"807256bb-01ca-43f3-8d07-d3d91a675f14", 
        "UpdateID":"6509643556",
        ...
    }
]

Status can contain the following values:

  • Cancelled – the platform cancelled the message, it is not sent
  • Sent – the platform did not cancel the message and will try to send it
  • Rejected – the carrier or cloud rejected the message
  • Accepted – the carrier or cloud accepted the message
  • Failed – the carrier or cloud failed to deliver the message
  • Delivered - the message has been delivered on the device

The ID is a globally unique identifier (GUID) that uniquely identifies the message. The UpdateID is only guaranteed to be unique for 24 hours. This ID is needed to notify the platform that a message is retrieved by an app or read by the user, which is explained in the next paragraph.

DateTime is the time the message arrived at the Hybrid Messaging platform.

The messages API supports the ODATA Query language. You can specify $select, $top, $skip, $filter and $orderby to make your result set more specific.

See chapter 5 of this page for more information: http://www.odata.org/documentation/odata-version-3-0/url-conventions

Update messages

When your app retrieve a message as a push notification, it should notify the platform about this event. To do so, send the following PUT request to the server:

{
    "UpdateID":"6509643556",
    "Status":"Delivered"
}

When your app displayed the message to a user, it should notify the platform using a Date/Time in the ReadByRecipient field.

{
    "UpdateID":"6509643556",
    "Status":"Delivered",
    "ReadByRecipient":"2016-05-22T23:41:28.563"
}

Ack android messages

Messages sent from the HybridMessaging platform make use of the XMPP Android protocol. This allow us to monitor if a message is received on the device of the user. When receiving the message a connection from Google will be made with the phone of the user. This connection can be reused to send an ACK back to the originated address. In order to make HybridMessaging properely work on Android we need to know if a notification is received on the phone. If not a message will be send twice since we don't know for sure if the message was received correctly or on time. Follow the steps below to send back ACKS when succesfully receiving messages.

To receive messages from hybrid messaging you should first create a WakefulBroadcastReceiver with an IntentService capable of listening to incoming notifications.

In the IntentService you are able to retrieve the contents of the message and send an Ack back to the server over the XMPP protocol. In order to send the Ack back to the server you have to instantiate a GoogleCloudMessaging object and retrieve the message type.

final GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);

Also retrieve the bundle from the intent to retrieve the message

Bundle extras = intent.getExtras();

To send a message back to the server, make sure you wait a very short time. Let the Thread sleep for a very short while otherwise the Ack will not be send back to the server. Afterwards retrieve the bundle contents from the Extras received through the Intent. Important are time_to_live and messageId that need to be send back to the server. Send an ACK back to the server over the same GCM connection with as destination your Google project ID including an address as following: YOUR_PROJECT_ID + "@gcm.googleapis.com

Example:

public class GCMNotificationIntentService extends IntentService {

    public GCMNotificationIntentService() {
        super("com.your.project.GCMNotificationIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        Bundle extras = intent.getExtras();
        final GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
        String messageType = gcm.getMessageType(intent);

        if (extras != null) {
            if (!extras.isEmpty()) {
                if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {

                    for (int i = 0; i < 5; i++) {
                        try {
                            Thread.sleep(200);
                        }
                        catch (InterruptedException e) {}
                    }

                    String messageId = null;
                    long ttl = 30L;

                     Bundle bundle = intent.getExtras();
                     messageId = bundle.getString("message_id");
                     String notificationId = bundle.getString("from");
                     String message = bundle.getString("message");
                     String tempttl = bundle.getString("time_to_live");

                     if(tempttl != null) {
                         ttl = Long.valueOf(bundle.getString("time_to_live"));
                     }

                    // ## Send ACK back to server containing the following KEY and VALUE ## //
                    Bundle dataBundle = new Bundle();
                    dataBundle.putString("message_type", "DLR");

                    try {
                        if(tempttl != null) {
                            gcm.send(PROJECT_ID + "@gcm.googleapis.com", messageId, ttl, dataBundle);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        GcmBroadcastReceiver.completeWakefulIntent(intent); // Close the service
    }

}

important: Make sure to send "message_type" along with "DLR" back to the server