Inbox

The inbox feature lets you store targeted Inbox messages inside an Inbox within your app, in a similar way that a mailbox stores emails. With this Inbox, your users can retrieve messages over time, browse through the list of inbox messages and interact with them. Inbox messages are a good complement to push notifications or in-app messages.

These Inbox messages can be a text, a webpage, a landing page, ... with or without buttons and interact directly with the Accengage SDK. E.g. your message can include an event button, trigger an URL Scheme, or display a banner/interstitial when the user opens a message.

If you want to use this feature, the Accengage servers will send all the messages information to your app, but you will need to handle the display yourself.

Sample

A sample is available on Github : https://github.com/urbanairship/accengage-android-sdk-samples/tree/master/AccInbox

This example contains 2 activities:

  • InboxList → A list displaying the Title, Extract, Date, etc.. of each message, with all status options (Read, Unread, Archive)
  • ShowMessage → An activity which displays a message including buttons

Basic configuration

With Accengage Inbox you can:

  • Subscribe/Unsubscribe to Inbox Messages on a device
  • Get messages and display them in your own template
  • Update messages on the Accengage servers (Read/Unread/Archive)

Get Messages

Use the following method to retrieve your messages:

getA4S().getInbox(callback);

You need a callback to be notified when messages are received. Here is an example of how to define it:

Callback<Inbox> callback = new Callback<Inbox>() {
    @Override
    public void onResult(Inbox result) {
        //Messages received, they are contained in a Inbox Object.
    }

    @Override
    public void onError(int error, String errorMessage) {
        //Handle errors like bad request or no network connection
    }
};

List Messages

Now that you have your inbox object filled with messages, it's time to list your messages. In this example, we will just display a toast with the title of each message

//First, we will get each message, so let's do a loop
for(int i = 0; i < inbox.countMessages(); i++) {
    //Get our message at a specified position
    inbox.getMessage(i, new MessageCallback() {
        @Override
        public void onResult(Message result, int index) {
            //Here, index is the position of this message in the inbox.
            //It will be useful for instance if you display your messages in a ListView
            //Show the title of this message
            Toast.makeText(getApplicationContext(),result.getTitle(), Toast.LENGTH_LONG).show();
        }
        @Override
        public void onError(int error, String errorMessage) {
            //Handle error if we didn't manage to get this message.
        }
    });
}

Display a message

Now that our messages are downloaded, we can safely display them. Then according to the message format, the SDK will either open it itself or give it to you. If the message is returned to your application, a callback will be triggered and you will have to handle the message's display.

//Let's get the message at position 0 and display it
inbox.getMessage(0, new MessageCallback() {
    @Override
    public void onResult(Message result, int index) {
        result.display(getApplicationContext(), new Callback<Message>() {
            @Override
            public void onResult(Message m) {
                //Message can be MessageContentType.Web or MessageContentType.Text
                Toast.makeText(getApplicationContext(),result.getTitle(), Toast.LENGTH_LONG).show();
                //If it's a message of type Text, we display its body
                if(result.getContentType() == MessageContentType.Text) Toast.makeText(getApplicationContext(),result.getBody(), Toast.LENGTH_LONG).show();
                //Otherwise, the body is the url my webview should open.
                else myWebview.loadUrl(result.getBody());
            }
            @Override
            public void onError(int error, String errorMessage) {
                //Handle errors
            }
        });
    }
    @Override
    public void onError(int error, String errorMessage) {
        //Handle error if we didn't manage to get this message
    }
});

Work with Message Buttons

A message can have several buttons and here is an example of how to display and interact with them.

for(int i=0; i < message.countButtons(); i++) {
    //Get the button
    final com.ad4screen.sdk.Message.Button inboxButton = message.getButton(i);
    //Create a new button component to add to our layout
    Button button = new Button(getApplicationContext());
    //Set the title of this button
    button.setText(inboxButton.getTitle());
    //Set a listener to trigger the action on click
    button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            //Notify this button to trigger the click action
            inboxButton.click(getApplicationContext());
        }
    });
}

Note: Don't forget to add each button to your layout.

Update a message

You can change the status of a message, here is how to do it

//set a message read or unread
message.setRead(true);
//Archive or Un-Archove a message
message.setArchived(false);

Last step, you need to send all the changes made on this inbox to the Accengage Servers:

getA4S().updateMessages(inbox);

Advanced configuration

Messaging with Firebase

Introduction

By adding Accengage open source library A4SSamples which uses Firebase AuthenticationRealtime Database and Analytics to your application you can easily integrate Inbox Messaging service with a few lines of code. The library allows you to:

  • authenticate users (with different auth providers - google, email, etc)
  • work with Inbox messages on several devices at the same time (messages are synchronised between devices with Firebase Realtime Database)
  • have an access to archived and expired messages (you don't need to care about storing messages that Accengage doesn't provide any more)
  • filter messages by their categories
  • have Gmail look like UI

MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    startActivity(new Intent(this, InboxNavActivity.class));
    finish();
}

Gmail

 

AccInboxFirebase

  

Integration

Adding Firebase to your Android project

Create Firebase project and add a config file google-services.json to app folder. Fore more details please go here.

Then, include google-services plugin to the top-level build.gradle:

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    // ...
    dependencies {
        // ...
        classpath 'com.google.gms:google-services:3.1.0'
    }
}

and apply it by modifying the app/build.gradle:

apply plugin: 'com.android.application'
// ...
dependencies {
    // ...
}
// ADD THIS AT THE BOTTOM
apply plugin: 'com.google.gms.google-services'

Adding A4SSamples library

Add maven repositories to top-level build.gradle:

// Top-level build file where you can add configuration options common to all sub-projects/modules.
// ...
allprojects {
    repositories {
        //...
        // Required for A4SSamples:0.3
        maven {
            url  "http://dl.bintray.com/accengage/maven"
        }
        // Required for 'com.firebaseui:firebase-ui:1.2.0'
        maven {
            url 'https://maven.fabric.io/public'
        }
    }
}

Modify the app/build.gradle by adding the SDK, A4SSamples and some additional dependencies which are not mentioned in the .pom files:

dependencies {
    // ...
    compile 'com.ad4screen.sdk:A4SSDK:3.8.1'
    compile 'com.accengage.samples:A4SSamples:0.3'

    compile 'com.jakewharton:butterknife:8.6.0'
    compile 'com.github.bumptech.glide:glide:3.7.0'
    compile "io.reactivex.rxjava2:rxjava:2.1.1"
    compile "io.reactivex.rxjava2:rxandroid:2.0.1"
    compile 'com.twitter.sdk.android:twitter:2.3.0'
}

Modifying the code

Multidex support

Modify the module-level build.gradle file configuration to include the support library and enable multidex output, as shown in the following code snippet:

app/build.gradle

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        // ...
        // Enabling multidex support.
        multiDexEnabled true
    }
}

dependencies {
    // ...
    compile 'com.android.support:multidex:1.0.1'
}

In your manifest add the MultiDexApplication class from the multidex support library to the application element

AndroidManifest.xml

<application
    android:name="android.support.multidex.MultiDexApplication"
    ...>

</application>

NoActionBar theme

The Accengage InboxNacActivity, which we are going to launch, uses DrawerLayout with its own Action Bar. For this reason check that your app theme doesn't use the action bar: android:theme="@style/AppTheme.NoActionBar".

AndroidManifest.xml

<application
    android:name="android.support.multidex.MultiDexApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme.NoActionBar">

   <!-- ... -->

</application>

Where NoActionBar style is declared in styles.xml:

styles.xml

<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

Accengage credentials

As mentioned in the Integration section the A4SSDK must be authenticated and authorised by Accengage servers. That's why you need to add application credentials ( Partner ID  and  Private key ) into  strings.xml  resource file:

strings.xml

<resources>
    // ...
    <string name="acc_partner_id">YOUR_PARTNER_ID</string>
    <string name="acc_private_key">YOUR_PRIVATE_KEY</string>
</resource>

Starting Inbox

To launch Inbox with Firebase we need to start InboxNavActivity. In this example we start it directly from the MainActivity:

MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startActivity(new Intent(this, InboxNavActivity.class));
        finish();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        A4S.get(this).setIntent(intent);
    }

    @Override
    protected void onResume() {
        super.onResume();
        A4S.get(this).startActivity(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        A4S.get(this).stopActivity(this);
    }
}

Authentication

Enable sign-in provider

Once InboxNavActivity is started a user will need to be authenticated. For that you need to enable sign-in provider via Firebase console.

     

Audience and Segments

After signing-in a user name and its email will be sent to Firebase by using User Properties and to Accengage by using Device Information . For this you need to add two user properties for Firebase and two fields of database for Accengage: acc_user_name and acc_user_email.

 

Firebase user properties will allow you to create different filters for inbox events and to  analyse the audience. While a  user name  and/or  email address  sent by updating Device Information to Accengage will allow to create segments to which you will send Inbox messages.

 

Users in Firebase DB

Authenticated users are added to Firebase realtime database. The structure of JSON tree for users you can see in the picture below. It contains user tokens where inside of each there are two fields: email and username.

Users json tree in the database

Authenticated users and their tokens

Messages

Labels

There are 4 static labels listed in the Navigation Drawer: Primary, Archive, Expired and Trash. All incoming messages are placed in Primary label. By archiving or deleting a message it will be placed into corresponding label. If a message is expired (Expiration date is set via the dashboard), Accengage server will not send it any more. In this case it will be moved to Expired label.

There are also dynamic labels. A4SSamples library will create a dynamic label (category) if Category field is specified for the message via the dashboard. A message with a dynamic label always has a static label. In other words, a message with a category will always belong either to Primary or Archive or Expired or Trash. However if a message is removed (located in Trash) and it has a category, the category won't be visible. A4SSamples ignores categories (dynamic labels) of deleted messages.

Messages are stored in the realtime database. It's JSON tree structure is shown below. Node user-inbox-messages contains user tokens where inside of each can be two childs: inbox and trash. Inside of these childs we store messages (removed or not). If a message is not deleted it will be located inside inbox node, if it's deleted it will be located inside trash node. The value of JSON message object under the parent (inbox or trash) is Accengage message ID. The message object has childs describing an Inbox message (title, text, sender, label, etc). 

Categories

As we mentioned above, for A4SSamples message categories are dynamic labels. In order to provide the amount of messages for specific categories in the Navigation Drawer there is additional JSON tree: user-inbox-categories. The structure of this node looks like the structure of user-inbox-messages node but instead of JSON message objects there are JSON category objects. Their values correspond to category names and their childs contain message IDs. So the number of childs of JSON category object corresponds to the number of messages with this category.

By clicking on a category, A4SSamples filters messages by the value of this category and display the result in a new fragment.

 

Database rules

In order to prevent that a user has an access to messages of another one it's necessary to determine read and write access to your database. For this you need to grant read and write access only for authenticated users.

"$uid": {
  ".read": "auth.uid == $uid",
  ".write": "auth.uid == $uid"
}

Also we need to index label and category fields to optimise access time to your data.

".indexOn": ["label", "category"],

So the whole rules may look like this:

Rules

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth.uid == $uid",
        ".write": "auth.uid == $uid"
      }
    },
    "user-inbox-messages": {
      ".indexOn": ["label", "category"],
      "$uid": {
        ".read": "auth.uid == $uid",
        ".write": "auth.uid == $uid"
      }
    },
    "user-inbox-categories": {
      "$uid": {
        ".read": "auth.uid == $uid",
        ".write": "auth.uid == $uid"
      }
    }
  }
}

More information you can find here: https://firebase.google.com/docs/database/security/

Sample

You can also find a sample application on github: https://github.com/urbanairship/accengage-android-sdk-samples/tree/master/AccInboxFirebase

The demo application requires Android Studio 3.0. Its architecture is presented below.

Tracking integration

Starting with Accengage SDK 3.6.x, Inbox tracking needs to be manually implemented to match perfectly your own Inbox feature integration in your application.

Display Tracking

You can track when a message is displayed. You can choose to display only at the first display or each time the message is displayed. Use the following method:

com.ad4screen.sdk.Message inboxMessage = //...
inboxMessage.hasBeenDisplayedToUser(context);

Or, if you want to track only for the first display:

com.ad4screen.sdk.Message inboxMessage = //...
if (!inboxMessage.isDisplayed()) {
    inboxMessage.hasBeenDisplayedToUser(getContext());
    inboxMessage.setDisplayed(true);
}

Click Tracking

You can track when a message is clicked. Use the following method:

com.ad4screen.sdk.Message inboxMessage = //...
inboxMessage.hasBeenOpenedByUser(context);

Button Click Tracking

You can track when a message's button is clicked. Use the following method:

com.ad4screen.sdk.Message.Button inboxButton = //...
inboxButton.hasBeenClickedByUser(context);