Creating custom Text Selection actions with ACTION_PROCESS_TEXT

Android 6.0 Marshmallow introduced a new floating text selection toolbar, which brings the standard text selection actions, like cut, copy, and paste, closer to the text you’ve selected. Even better though is the new ACTION_PROCESS_TEXT which makes it possible for any app to add custom actions to that text selection toolbar.

The text selection toolbar in Android 6.0

Apps like Wikipedia and Google Translate are already taking advantage of it to instantly lookup or translate selected text.

You may have already seen documentation and a blog post about ensuring the text selection toolbar and options appear in your app (in short: using a standard TextView/EditText and they’ll work out of the box, but note your EditText would need to have an android:id set and you need to call getDelegate().setHandleNativeActionModesEnabled(false) if you are using AppCompatActivity and want to use the native floating text selection toolbar on API 23+ devices).

But finding information on implementing ACTION_PROCESS_TEXT and adding your own actions? That’s what this post will cover.

Cross app communication -> Intent Filters

As you might expect when building functionality that crosses app boundaries, your Android Manifest and the intent filters attached to each component serve as a public API that other apps can query.

ACTION_PROCESS_TEXT is no different. You’ll add an intent filter to an Activity in your manifest:

<activity
android:name=".ProcessTextActivity"
android:label="@string/process_text_action_name">
<intent-filter>
<action android:name="android.intent.action.PROCESS_TEXT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>

And, if you wanted multiple actions (you overachiever, you!), you’ll need separate activities for each. As of Android 6.0.1, you’ll want to avoid adding android:exported=”false” — your action will still appear in other apps, but they’ll get a SecurityException immediately upon clicking it (the feature request to implement filtering in ACTION_PROCESS_TEXT to match the behavior of the system chooser has been marked FutureRelease and fixed internally).

Note that the android:label of your Activity will show up as the action in the text selection toolbar so ensure it is short, an action verb, and recognizable as something iconic with your app. For example, Google Translate uses ‘Translate’ as it is a less common action (how many people have multiple translation apps installed?), while Wikipedia uses ‘Search Wikipedia’ as searching may be a much more common action for many apps.

Getting the selected text

Once you get your intent filter set up, other apps will already be able to start your activity by selecting text and choosing your action from the text selection toolbar. But that doesn’t add any value unless you actually look at the text that was selected.

That’s where EXTRA_PROCESS_TEXT comes in: it is a CharSequence included in the Intent that represents what text was selected. Don’t be deceived — even though you are using a text/plain intent filter, you’ll get the full CharSequence with any Spannables included, so don’t be surprised if you notice some styling if you use the CharSequence directly in your app (you can always call toString() to remove all formatting).

Therefore your onCreate() method may look something like:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.process_text_main);
CharSequence text = getIntent()
.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT);
// process the text
}

With one caveat if you are using android:launchMode=”singleTop”, then you’ll also want to process text in onNewIntent() as well — a common practice is to have both onCreate() and onNewIntent() call a single handleIntent() method you create.

And that’s about all you’d need if you are using ACTION_PROCESS_TEXT as an entryway into your app: what you do with it after that point is up to you.

Returning a result

There’s one other extra included in the ACTION_PROCESS_TEXT Intent though: EXTRA_PROCESS_TEXT_READONLY. This boolean extra denotes whether the selected text you just received can be edited by the user (such as would be the case in an EditText).

You’d retrieve the extra with code such as

boolean readonly = getIntent()
.getBooleanExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, false);

You can use this as a hint to offer the ability to return altered text to the sending app, replacing the selected text. This works as your Activity was actually started with startActivityForResult() — you’ll be able to return a result by calling setResult() at any time prior to your Activity finishing:

Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_PROCESS_TEXT, replacementText);
setResult(RESULT_OK, intent);

You could imagine a button to ‘Replace’ would call setResult() followed by finish() to return back to the calling Activity.

Common questions

Before you start writing responses, here’s some common questions about ACTION_PROCESS_TEXT:

Q: Can I trigger a Service with ACTION_PROCESS_TEXT?

A: Not directly — the system only looks for Activities that contain the correct intent filter. That doesn’t mean you can’t have your Activity launch a Service using a theme of Theme.Translucent.NoTitleBar or even Theme.NoDisplay (as long as you immediately finish the Activity), but make sure you have some user visible hint that their action was received — a notification starting, a Toast, etc.

Q: Can I trigger it only for certain types of text?

A: Nope. Your option will appear every time anyone selects text. Of course, chances are users won’t select an option to ‘Translate’ unless they want to translate, etc., but I’d be careful to code defensively as you cannot be sure what type of text context you’ll receive.

Q: So should every app implement ACTION_PROCESS_TEXT? Wouldn’t that be madness?

A: Yes, that would be madness and no, not every app should implement ACTION_PROCESS_TEXT. Make sure any actions you implement are universal and truly useful to users who have your app installed.

Learn more

Besides the aforementioned Wikipedia and Google Translate which already contain good, real world examples, you can also check out the ApiDemos app installed on Marshmallow emulators or look at the code directly.

#BuildBetterApps

Join the discussion on the Google+ post and follow the Android Development Patterns Collection for more!