In-App Automation

In-App Messages appear regardless of a user's opt-in/out status for notifications. While standard in-app messages appear as banners, In-App Automation messages have various style and layout options. They are stored on the user's device then displayed according to the triggers you define.

In-app messaging can improve your engagement strategy by providing messages that can be customized to be visually consistent with the the rest of your app. Most customization can be accomplished through the dashboard composer, however more advanced customization can be obtained by modifying the Airship SDK. This guide outlines how to customize Android in-app messages.

Feature enablement

Disabling the manager:

UAirship.shared()
        .getInAppMessagingManager()
        .setEnabled(false);

The in-app messaging manager can be enabled or disabled. When the manager is disabled it prevents any in-app messages from displaying and pausing the automation engine counting. The manager is enabled by default.

Pausing message display

Pausing the manager:

UAirship.shared()
        .getInAppMessagingManager()
        .setPaused(true);

Pausing is simliar to disabling the manager, but event will contiue to trigger messages to be queued up for display. This is useful for preventing in-app message displays on screens where it would be detrimental to the user experience, such as splash screens, settings screens, or landing pages.

Display interval

Setting the display interval to 10 seconds:

UAirship.shared()
        .getInAppMessagingManager()
        .setDisplayInterval(10, TimeUnit.SECONDS);

The display interval controls the amount of time to wait before the manager is able to display the next triggered in-app message. The default value is set to 30 seconds but can be adjusted to any amount of time.

Listening for Events

Example listener:

UAirship.shared()
        .getInAppMessagingManager()
        .addListener(new InAppMessageListener() {
            @Override
            public void onMessageDisplayed(@NonNull String scheduleId, @NonNull InAppMessage message) {
                // Message displayed
            }

            @Override
            public void onMessageFinished(@NonNull String scheduleId, @NonNull InAppMessage message, @NonNull ResolutionInfo resolutionInfo) {
                // Message finished
            }
        });

A listener can be added to the in-app messaging manager to listen for when a message is displayed and finished displaying. This is useful for adding analytics events outside of Airship as well as for further processing of the in-app message.

Excluding Activities

Exclude activity from displaying in-app messages:

<meta-data
      android:name="com.urbanairship.push.iam.EXCLUDE_FROM_AUTO_SHOW"
      android:value="true" />

Activities can be excluded from auto-displaying an in-app message. This is useful for preventing in-app message displays on screens where it would be detrimental to the user experience, such as splash screens, settings screens, or landing pages.

Customization

In-app messages are fully customizable. Minor modifications can be accomplished by overriding the styles, but more advanced customizations can employ an adapter for the given message type.

Fonts

Fonts that are added in XML are available for use with in-app messaging, including downloadable fonts. To add fonts, please read the Fonts In XML guide. Once added, you can specify the fonts by using the resource name, e.g., use cool_font for res/xml/cool_font.xml.

Styles

The Android resource merging feature can be used to override any of the message styles that the SDK provides. Copy any of the styles that need to be overridden into the application's resource directory, then change any of the styles.

Styles:

Custom adapters

Example adapter:

public class CustomInAppMessageAdapter implements InAppMessageAdapter {

    private InAppMessage message;
    private Assets assets;

    public CustomInAppMessageAdapter(InAppMessage message) {
        this.message = message;
    }

    @Override
    @WorkerThread
    @PrepareResult
    public int onPrepare(@NonNull Context context, @NonNull Assets assets) {
        // Do any additional prepare steps
        this.assets = assets;
        return OK; // RETRY, CANCEL
    }

    @Override
    @MainThread
    public boolean isReady(@NonNull Context context) {
        // Called right before display.
        return true;
    }

    @Override
    @MainThread
    public void onDisplay(@NonNull Context context, @NonNull DisplayHandler displayHandler) {
        Intent intent = new Intent(context, CustomActivity.class)
                .putExtra(InAppMessageActivity.IN_APP_MESSAGE_KEY, message)
                .putExtra(InAppMessageActivity.IN_APP_ASSETS, assets)
                .putExtra(InAppMessageActivity.DISPLAY_HANDLER_EXTRA_KEY, displayHandler);

        context.startActivity(intent);
    }

    @Override
    @WorkerThread
    public void onFinish(@NonNull Context context) {
        // Perform any clean up
    }
}

An example activity that displays a modal dialog:

public class CustomActivity extends InAppMessageActivity implements View.OnClickListener {


    // Called during onCreate
    @Override
    protected void onCreateMessage(@Nullable Bundle savedInstanceState) {
        // Bind message
        InAppMessage message = getMessage();
        BannerDisplayContent displayContent = message.getDisplayContent();
        if (displayContent == null) {
            Log.e("CustomActivity", "Started with wrong message type");
            finish();
        }

        // Bind view to message
    }

    @Override
    public void onClick(View v) {
        // Notify the display handler of any message resolutions before finishing the activity
        getDisplayHandler().finished(ResolutionInfo.messageClicked(), getDisplayTime());
        finish();
    }
}

Override the default modal factory to provide the new adapter:

UAirship.shared()
        .getInAppMessagingManager()
        .setAdapterFactory(InAppMessage.TYPE_MODAL, message -> {
            return new CustomInAppMessageAdapter(message);
        });

Providing an adapter allows full customization for any message type. The adapter will be created by the in-app messaging manager when a message's schedule is triggered. Once created, the adapter will be called to first prepare the in-app message, giving the adapter time to download any resources such as images. After the adapter prepares the message, the adapter will be called to display the message. Be sure to update the adapter factory for the message type you are trying to override. In this example, the adapter is overriding modal messages, therefore InAppMessage.TYPE_MODAL.

After the message is displayed, the provided display handler must be notified that the message is finished displaying by calling the finished method. This will allow other in-app messages to be displayed.

Standard In-App Messages

Setting a schedule info builder extender:

UAirship.shared()
        .getLegacyInAppMessageManager()
        .setScheduleBuilderExtender((context, builder, legacyMessage) -> {
            // Provide any changes to the builder
            return builder;
        });

Setting a message builder extender:

UAirship.shared()
        .getLegacyInAppMessageManager()
        .setMessageBuilderExtender((context, builder, legacyMessage) -> {
            // Provide any changes to the builder
            return builder;
        });

Standard in-app messages delivered through push messages are managed by the legacy in-app message manager. The manager converts the standard in-app message into a new in-app message schedule. The conversion can be customized by setting a builder extender to extend either the schedule builder or the message builder.

For more information on how to customize this conversion process, see the In-App Messages Migration guide.

Customizing HTML In-App Messages

HTML in-app messages provide a way to display custom content inside a native web view. These types of in-app messages display with a dismiss button built in, but can also be customized to provide their own buttons capable of dismissing the view. Dismissing a view requires calling the dismiss function on the UAirship JavaScript interface with a button resolution object passed in as a parameter. The button resolution object is a JSON object containing information about the interaction type and the button performing the dismissal. It should match the following format:

{
  "type" : "button_click",
  "button_info" : {
    "id" : "button identifier",
    "label" : {"text" : "foo"}
  }
}

The button resolution requires each of the key fields shown above. These include:

  • type : The type key with the value of resolution type button_click
  • button_info : The button info object containing required id and label fields
    • id: The button identifier
    • label : Label object containing the required text key
      • text : The text key with a string value representing the label text

Providing a basic dismiss button in HTML:

<button onclick="UAirship.dismiss({
  'type' : 'button_click',
  'button_info' : {
    'id' : 'button identifier',
    'label' : {'text' : 'foo'}
  }
}
});">Dismiss with resolution</button>
 Note

Whitelisting URLs is necessary for loading the Native Bridge, which connects the UAirship.dismiss call to the SDK. This can be accomplished by providing an array of string URLs for your hosted HTML content under the whitelist key inside your airshipconfig.properties. The example below uses a wildcard to match all content under the domain "mydomain.com". For more information on the syntax for whitelist patterns, see the API docs.

whitelist = https://*.mydomain.com

Customizing Landing Pages

Landing pages are displayed on the device as In-App Automation HTML messages. In addition to the HTML customizations documented above, landing pages can also be customized by registering a custom subclass of the LandingPageAction that extends its message and scheduleInfo builders.

Custom LandingPageAction class

import com.urbanairship.actions.LandingPageAction;
import com.urbanairship.iam.InAppMessage;
import com.urbanairship.iam.InAppMessageScheduleInfo;

import androidx.annotation.NonNull;

public class CustomLandingPageAction extends LandingPageAction {

    /**
     * Can be used to customize the InAppMessage.
     *
     * @param builder The builder.
     * @return The builder.
     */
    @NonNull
    protected InAppMessage.Builder extendMessage(InAppMessage.Builder builder) {
        // Customize message here
        return builder;
    }

    /**
     * Can be used to customize the InAppMessageScheduleInfo.
     *
     * @param builder The builder.
     * @return The builder.
     */
    @NonNull
    protected InAppMessageScheduleInfo.Builder extendSchedule(InAppMessageScheduleInfo.Builder builder) {
        // Customize schedule here
        return builder;
    }

}

Replace built-in landing page action with custom landing page action:

    UAirship.shared()
            .getActionRegistry()
            .registerAction(CustomLandingPageAction.class, "^p", "landing_page_action");

For more information on how to extend scheduleInfo builders, see the In-App Messages Migration guide.

Landing pages can also be customized by overriding the HTML message adapter. When doing so, be sure to update the adapter factory for the HTML message type InAppMessage.TYPE_HTML.