Preference Center

Preference Center allows users to opt in and out of subscription lists configured via the Airship Dashboard.

Installation

Preference Center requires adding the urbanairship-preference-center module:


dependencies {
  def airshipVersion = "15.0.0"

  implementation "com.urbanairship.android:urbanairship-preference-center:$airshipVersion"
}

Displaying a Preference Center

A Preference Center can be displayed with a simple method call on the PreferenceCenter instance. By wiring this method call to a button in your app, you can quickly produce a user-initiated Preference Center with no additional effort.

Opening a Prefrence Center by ID
PreferenceCenter.shared().open(preferenceCenterId = "my-first-pref-center")
PreferenceCenter.shared().open("my-first-pref-center");

Adding a Custom Look and Feel

By default, Preference Centers are displayed with basic styling for elements, similar in appearance to Android’s PreferenceFragment.

Most developers will want to customize the look and feel to match their app’s existing style and layout. If your app is using a theme that extends from a MaterialComponents theme, Preference Centers can be easily customized to use your app colors by overriding the top-level preference center style:

Extending from a MaterialComponents app theme
<!-- Use colors from MyAppTheme instead of the default pref center colors. -->
<style name="UrbanAirship.PreferenceCenter" parent="MyAppTheme">
  <!-- Specific attributes may also be overriden here, as needed. For example,
       the following overrides the background color used by PreferenceCenterFragment. -->
  <item name="android:colorBackground">@color/my_background_color</item>
</style>

If your app doesn’t use a MaterialComponents theme or you need the ability to further customize preference center styles, The Android resource merging feature can be used to override the default styles that the SDK provides. Copy the style sheet into the application’s resource directory, then change any of the styles.

Overriding the Preference Center

If the provided Activity does not work for your application, you can either embed the PreferenceCenterFragment directly into your application, or you can provide your own UI.

Embedding the PreferenceCenterFragment

When embedding the PreferenceCenterFragment, either use a FragmentContainerView or create the fragment directly. You must specify the ID of the Preference center to be displayed when creating the fragment. The static create on PreferenceCenter will handle passing the given id to the fragment as an argument:

Instantiating a PreferenceCenterFragment
val fragment = PreferenceCenterFragment.create(preferenceCenterId = "my-first-pref-center")
PreferenceCenterFragment fragment = PreferenceCenterFragment.create("my-first-pref-center");

Building your own Preference Center UI

If the provided fragment is insufficient for your app, you can build your own UI by accessing the PreferenceCenterConfig directly:

val configPendingResult = PreferenceCenter.shared().getConfig("my-first-pref-center")
PendingResult<PreferenceCenterConfig> configPendingResult = PreferenceCenter.shared().getConfig("my-first-pref-center");

The PreferenceCenterConfig object includes the name and (optional) description for the Preference Center and a list of sections containing preference items:

fun displayPreferenceCenter(config: PreferenceCenterConfig) {
  val prefCenterName = config.display.name
  val prefCenterDescription = config.display.description

  for (section in config.sections) {
    val sectionName = section.display.name
    val sectionDescription = section.display.description

    for (item in section.items) {
      val itemId = item.id
      val itemName = item.display.name
      val itemDescription = item.display.description

      // Create item and add it to an adapter, etc...
    }
  }
}
void displayPreferenceCenter(PreferenceCenterConfig config) {
  String prefCenterName = config.display.name;
  String prefCenterDescription = config.display.description;

  // Set pref center name and description on Activity, Fragment, etc...

  for (Section section : config.sections) {
    String sectionName = section.display.name;
    String sectionDescription = section.display.description;

    for (Item item : section.items) {
      String itemId = item.id;
      String itemName = item.display.name;
      String itemDescription = item.display.description;

      // Create item and add it to an adapter, wire up click listener, etc...
    }
  }
}

To update subscriptions when a preference item is updated, use the editSubscriptionLists() method on AirshipChannel:

fun onPreferenceItemToggled(item: Item, isSubscribed: Boolean) {
  switch(item) {
    is Item.ChannelSubscription -> {
      val editor = UAirship.shared().channel.editSubscriptionLists()
      if (isSubscribed) {
        editor.subscribe(item.id)
      } else {
        editor.unsubscribe(item.id)
      }
      editor.apply()
    }
  }
}
void onPreferenceItemToggled(Item item, boolean isSubscribed) {
  if (item instanceof ChannelSubscriptionItem) {
    Item.ChannelSubscription subscription = (Item.ChannelSubscription) item;
    SubscriptionListEditor editor = UAirship.shared().getChannel().editSubscriptionLists();
    if (isSubscribed) {
      editor.subscribe(subscription.getId());
    } else {
      editor.unsubscribe(subscription.getId());
    }
    editor.apply();
  }
}

The getSubscriptionLists method on AirshipChannel can be used to fetch the set of subscriptions that the channel is currently subscribed to. The IDs returned in the set correspond to Item IDs in the PreferenceCenterConfig object. An empty set indicates that the current channel is not subscribed to any lists.

// Include subscription list changes that have been requested via SubscriptionListEditor
// and will be applied during the next channel update.
val includePendingUpdates = true
val subscriptionsPendingResult = UAirship.shared().channel.getSubscriptionLists(includePendingUpdates)
// Include subscription list changes that have been requested via SubscriptionListEditor
// and will be applied during the next channel update.
boolean includePendingUpdates = true;
PendingResult<Set<String>> subscriptionsPendingResult = UAirship.shared().getChannel().getSubscriptionLists(includePendingUpdates);

Override open behavior

If you provide your own UI or embed the fragment directly, you need to override the open behavior to navigate to your custom UI. Set the listener during takeOff.

PreferenceCenter.shared().openListener =  object : PreferenceCenter.OnOpenListener {
    override fun onOpenPreferenceCenter(preferenceCenterId: String): Boolean {
      // Navigate to custom preference center UI
      return true
    }
}
PreferenceCenter.shared().setOpenListener(preferenceCenterId -> {
    // Navigate to custom preference center UI
    return true;
});

Deep linking

Deeplinking to a Preference Center is supported automatically within the SDK with URLs with uairship:// scheme. Preference Center deep links must include the ID of the Preference Center to be opened, in the form of uairship://preferences/pref-center-id.

To expose deep linking to a preference center externally, setup a deep link that calls through to PreferenceCenter.shared().open("pref-center-id").

fun onDeepLink(deepLink: String) {
  if (deepLink.startsWith("my-app://deeplink/preferences/")) {
    val prefCenterId = deepLink.toUri().lastPathSegment
    PreferenceCenter.shared().open(prefCenterId)
  }
}
public void onDeepLink(String deepLink) {
  if (deepLink.startsWith("my-app://deeplink/preferences/")) {
    Uri uri = Uri.parse(deepLink);
    String prefCenterId = uri.getLastPathSegment();
    PreferenceCenter.shared().open(prefCenterId);
  }
}