Android inApp Customization
Listening for Events
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.
Example
InAppAutomation.shared().getInAppMessageManager().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
}
});
InAppAutomation.shared().inAppMessageManager.addListener(object : InAppMessageListener {
override fun onMessageDisplayed(scheduleId: String, message: InAppMessage) {
// Message displayed
}
override fun onMessageFinished(scheduleId: String, message: InAppMessage, resolutionInfo: ResolutionInfo) {
// Message finished
}
})
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
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 the message type is 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.
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
}
}
public class CustomInAppMessageAdapter(private val message: InAppMessage) : InAppMessageAdapter {
private var assets: Assets? = null
@WorkerThread
@InAppMessageAdapter.PrepareResult
override fun onPrepare(context: Context, assets: Assets): Int {
// Do any additional prepare steps
this.assets = assets
return InAppMessageAdapter.OK // RETRY, CANCEL
}
@MainThread
override fun isReady(context: Context): Boolean {
// Called right before display.
return true
}
@MainThread
override fun onDisplay(context: Context, displayHandler: DisplayHandler) {
val intent: Intent = Intent(context, CustomActivity::class.java)
.putExtra(InAppMessageActivity.IN_APP_MESSAGE_KEY, message)
.putExtra(InAppMessageActivity.IN_APP_ASSETS, assets)
.putExtra(InAppMessageActivity.DISPLAY_HANDLER_EXTRA_KEY, displayHandler)
context.startActivity(intent)
}
@WorkerThread
override fun onFinish(context: Context) {
// Perform any clean up
}
}
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();
}
}
class CustomActivity : InAppMessageActivity(), View.OnClickListener {
// Called during onCreate
override fun onCreateMessage(savedInstanceState: Bundle?) {
// Bind message
val displayContent = message?.getDisplayContent<BannerDisplayContent>()
if (displayContent == null) {
Log.e("CustomActivity", "Started with wrong message type")
finish()
}
// Bind view to message
}
override fun onClick(v: View?) {
// Notify the display handler of any message resolutions before finishing the activity
displayHandler?.finished(ResolutionInfo.messageClicked(), displayTime)
finish()
}
}
Override the default modal factory to provide the new adapter
InAppAutomation.shared().getInAppMessageManager().setAdapterFactory(InAppMessage.TYPE_MODAL, message -> {
return new CustomInAppMessageAdapter(message);
});
InAppAutomation.shared().inAppMessageManager.setAdapterFactory(InAppMessage.TYPE_MODAL) { message ->
CustomInAppMessageAdapter(message)
}
Standard In-App Messages
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.
Setting a schedule info builder extender
LegacyInAppMessageManager.shared()
.setScheduleBuilderExtender((context, builder, legacyMessage) -> {
// Provide any changes to the builder
return builder;
});
InAppMessageManager.shared()
.setScheduleBuilderExtender { context, builder, legacyMessage ->
// Provide any changes to the builder
builder;
}
Setting a message builder extender
InAppMessageManager.shared()
.setMessageBuilderExtender((context, builder, legacyMessage) -> {
// Provide any changes to the builder
return builder
});
InAppMessageManager.shared()
.setMessageBuilderExtender { context, builder, legacyMessage ->
// Provide any changes to the builder
builder
}
Customizing HTML In-App Messages
In order for the Airship JavaScript interface to be loaded into the webview, the URL must be specified in the UrlAllowList.
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 typebutton_click
button_info
— The button info object containing required id and label fieldsid
— The button identifierlabel
— Label object containing the required text keytext
— 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>
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
// Replace the default LandingPageAction
UAirship.shared().getActionRegistry()
.registerAction(CustomLandingPageAction.class, "^p", "landing_page_action");
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 Schedule.
*
* @param builder The builder.
* @return The builder.
*/
@NonNull
protected Schedule.Builder<InAppMessage> extendSchedule(Schedule.Builder<InAppMessage> builder) {
// Customize schedule here
return builder;
}
}
// Replace the default LandingPageAction
UAirship.shared().actionRegistry
.registerAction(CustomLandingPageAction::class.java, "^p", "landing_page_action")
class CustomLandingPageAction : LandingPageAction() {
/**
* Can be used to customize the InAppMessage.
*
* @param builder The builder.
* @return The builder.
*/
override fun extendMessage(builder: InAppMessage.Builder): InAppMessage.Builder {
// Customize message here
return builder
}
/**
* Can be used to customize the InAppMessageScheduleInfo.
*
* @param builder The builder.
* @return The builder.
*/
override fun extendSchedule(builder: Schedule.Builder<InAppMessage>): Schedule.Builder<InAppMessage> {
// Customize schedule here
return builder
}
}
Categories