Implementation of plug-in API for Locale for clients
Latest commit a32e1a2 Feb 16, 2017 @ccjernigan ccjernigan Fixup Javadoc generation



Locale allows developers to create plug-in conditions and settings. The android-plugin-client-sdk-for-locale implements a set of classes to simplify building a plug-in. This SDK is the middle layer of the Locale Developer Platform.

Although there are multiple ways to approach building a plug-in host or plug-in client, we do not recommend starting with this SDK layer. Instead we strongly recommend starting with the main Locale Developer Platform documentation.

API Reference

JavaDocs for the library are published here.


The library is compatible and optimized for Android API Level 9 and above.



The library is published as an artifact to jCenter. To use the library, the jCenter repository and the artifact need to be added to your build script.

The build.gradle repositories section would look something like the following:

repositories {

And the dependencies section would look something like this:

dependencies {
    compile group:'com.twofortyfouram', name:'android-plugin-client-sdk-for-locale', version:'[4.0.2, 5.0['

Creating a Plug-in


A plug-in implementation consists of two things:

  1. Activity: for the ACTION_EDIT_CONDITION or ACTION_EDIT_SETTING Intent action.
  2. BroadcastReceiver: for the ACTION_QUERY_CONDITION or ACTION_FIRE_SETTING Intent action.

At runtime the host launches the plug-in's Activity, the plug-in's Activity returns a Bundle to the host, and the host will send that Bundle back to the plug-in's BroadcastReceiver when it is time to query/fire the plug-in. The host may also pass the Bundle back to the Activity in the future, if the user wishes to edit the plug-in's configuration again.

Step by Step

  1. Add dependencies to build.gradle as described in the Usage section above.
  2. Architect the contents of the plug-in's EXTRA_BUNDLE. We recommend implementing a "BundleManager" object with static methods to verify the Bundle is correct, generate a new Bundle, and extract values from the Bundle.
  3. Implement the "Edit" Activity:

    1. Subclass AbstractPluginActivity (or AbstractFragmentPluginActivity for android-support-v4 compatibility) and provide implementations for:
      1. isBundleValid(android.os.Bundle): Determines whether a Bundle is valid.
      2. onPostCreateWithPreviousResult(android.os.Bundle previousBundle, java.lang.String previousBlurb): If the user is editing an old instance of the plug-in, this allows the Activity to restore state from that old plug-in configuration.
      3. getResultBundle(): When the Activity is finishing, this method will return the Bundle that represents the plug-in's state. This Bundle will eventually be sent to the BroadcastReceiver when the plug-in is queried.
      4. getResultBlurb(android.os.Bundle bundle): When the Activity is finishing, this method will return a concise, human-readable description of the plug-in's state that may be displayed in the host's UI.
    2. Add the AndroidManifest entry for the Activity. Note: It is very important that plug-in conditions and settings have a stable Activity class name. The package and class names for the edit Activity are a plug-in's public API. If they do not remain consistent, then saved instances of the plug-in created previously will be orphaned. For more information, see Dianne Hackborn's blog post Things That Cannot Change. To make maintaining a stable Activity class name easier, we recommend using an activity-alias for exposing the plug-in's edit Activity. (It is permitted for the plug-in's BroadcastReceiver class name to change.)

      1. Add an Intent filter for ACTION_EDIT_CONDITION or ACTION_EDIT_SETTING Intent action.
      2. Add an Activity icon: This icon will be shown in the host's UI. The ldpi version of the icon should be 27x27 pixels, the mdpi version should be 36x36 pixels, the hdpi version of the icon should be 48x48 pixels, the xhdpi version of the icon should be 72x72 pixels, and the xxhdpi version of the icon should be 108x108 pixels. Note: THIS ICON IS SMALLER THAN THE LAUCHER ICON. Providing a correctly scaled icon will improve performance when the host displays the plug-in's icon.
      3. Add an Activity label: The label is the name that will be displayed in the host's UI.

        <!-- This is the real Activity implementation but it is not exposed directly. -->
        <!-- This is the activity-alias, which the host perceives as being the plug-in's Edit Activity.
             This layer of indirection helps ensure the public API for the plug-in is stable.  -->
                <!-- For a plug-in setting, use EDIT_SETTING instead. -->
                <action android:name="com.twofortyfouram.locale.intent.action.EDIT_CONDITION"/>
  4. Implement the BroadcastReceiver:
  5. Add the AndroidManifest entry for the BroadcastReceiver

    • Condition: Register the BroadcastReceiver with an Intent-filter for ACTION_QUERY_CONDITION:

                  <action android:name="com.twofortyfouram.locale.intent.action.QUERY_CONDITION"/>
    • Setting: Register the BroadcastReceiver with an Intent-filter for ACTION_FIRE_SETTING:

                  <action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING"/>


  • 1.0.0: Initial release
  • 1.0.1: Fix diffing of plug-in edits. Thanks @jkane001 for reporting this issue!
  • 1.1.0: Support for Material Design and appcompat-v7
  • 2.0.0: Update spackleLib dependency to 2.0.0
  • 3.0.0
    • Remove AbstractLocalePluginActivity and AbstractLocaleFragmentPluginActivity. These deprecated Activities implemented UI logic, while this SDK should only responsible for communicating with the host.
    • Rename AbstractPluginConditionReceiver.getPluginConditionState(Context, Bundle) to be more internally consistent.
  • 4.0.0: Remove strings and resources that were previously used by AbstractLocalePluginActivity and AbstractLocaleFragmentPluginActivity.
  • 4.0.1: Fix visibility of Activity onPostCreate(). Thanks @ddykhoff for reporting this issue!
  • 4.0.2: Fix async plug-in settings. Thanks @giech for reporting this issue!