The following is the first few sections of a chapter from The Busy Coder's Guide to Android Development, plus headings for the remaining major sections, to give you an idea about the content of the chapter.


Miscellaneous Integration Tips

This chapter is a collection of other miscellaneous integration and introspection tips and techniques that you might find useful in your Android apps.

Prerequisites

Understanding this chapter requires that you have read the core chapters of this book.

Direct Share

The classic means of “sharing” content between apps is via ACTION_SEND. You create an ACTION_SEND Intent, identifying the content to share, and use it with startActivity(). The decision of what the candidates are to share with is based solely on the MIME type of the content in question.

Sometimes, sharing of content with another app really means sharing that content with some other person, folder, or finer-grained context within the other app. ACTION_SEND, on its own, does not do anything for this. The user chooses the other app, then inside that app chooses the finer-grained context. While ACTION_SENDTO supports the sender indicating who to share the content with, that only works for select Uri schemes (mailto and smsto, mostly), and it requires that the sender have a suitable Uri to identify the recipient. As a result, few apps support ACTION_SENDTO.

Android 6.0 introduced “direct share targets”. Now, the recipients of sharing operations can elect to serve up specific share targets, pointing not only to the app but to the finer-grained context within the app. The user will then see these targets listed in the “chooser” window, alongside other standard share targets.

This involves creating a subclass of ChooserTargetService and tying it via some <meta-data> to your activity supporting the ACTION_SEND <intent-filter>. That service will then be called with onGetChooserTargets(), where it is told what activity and <intent-filter> was matched, and the service can return a list of ChooserTarget objects. Those ChooserTarget objects each represent a single direct share target, where the ChooserTarget wraps up a dedicated caption, icon, and PendingIntent for each. Those may be presented to the user in the chooser; if the user chooses one, the PendingIntent is invoked.

The Intents/FauxSenderMNC sample project is a revised version of the FauxSender sample. FauxSender has an implementation of an ACTION_SEND activity, plus a LAUNCHER activity that just uses startActivity() to trigger an ACTION_SEND Intent. FauxSenderMNC augments the original sample with direct-share functionality.

The ChooserTargetService

The bulk of the business logic goes in your subclass of ChooserTargetService, here named CTService:

package com.commonsware.android.fsendermnc;

import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService;
import java.util.ArrayList;
import java.util.List;

public class CTService extends ChooserTargetService {
  private String titleTemplate;

  @Override
  public void onCreate() {
    super.onCreate();

    titleTemplate=getString(R.string.title_template);
  }

  @Override
  public List<ChooserTarget> onGetChooserTargets(ComponentName sendTarget,
                                                 IntentFilter matchedFilter) {
    ArrayList<ChooserTarget> result=new ArrayList<ChooserTarget>();

    for (int i=1;i<=6;i++) {
      result.add(buildTarget(i));
    }

    return(result);
  }

  private ChooserTarget buildTarget(int targetId) {
    String title=String.format(titleTemplate, targetId);
    int iconId=getResources().getIdentifier("ic_share" + targetId,
        "drawable", getPackageName());
    Icon icon=Icon.createWithResource(this, iconId);
    float score=1.0f-((float)targetId/40);
    ComponentName cn=new ComponentName(this, FauxSender.class);
    Bundle extras=new Bundle();

    extras.putInt(FauxSender.EXTRA_TARGET_ID, targetId);

    return(new ChooserTarget(title, icon, score, cn, extras));
  }
}

You are welcome to override the onCreate() and onDestroy() lifecycle methods in your ChooserTargetService if you want, though it is not required. Here, we override onCreate() just to grab a string resource value that will be used as a template, stashing it in a data member.

The one method that you have to implement is onGetChooserTargets(). This will be called when direct-share is triggered, as directed by some manifest entries that we will examine in a bit. Your job is to return a List of ChooserTarget objects that represent specific ways to share the content into your app, such as sharing to particular contacts or folders or something.

Note that whatever you return from onGetChooserTargets() is included along with your regular ACTION_SEND activity itself. Hence, you only want to return ChooserTarget objects that improve the user flow beyond your base ACTION_SEND activity — you do not need to have a ChooserTarget that simply replicates what the user would get from the ACTION_SEND activity itself.

In this case, onGetChooserTargets() returns a six-element ArrayList of ChooserTarget objects, each built using a private buildTarget() method.

A ChooserTarget is a simple wrapper around five pieces of data:

Note that the ComponentName does not have to start the same activity that is your ACTION_SEND activity. In this sample, it happens to use the same activity. But that is not a requirement, and frequently you will use some other activity. For example, if your normal ACTION_SEND flow would first have the user choose a folder, then provide additional information about the shared item (e.g., confirm the title, add tags), if you create direct-share targets that specify particular folders, you would want to bypass the folder-selection step in your own UI. If the ACTION_SEND activity implements the folder-selection logic and forwarded the user along to some other activity to handle the rest, your ChooserTarget ComponentName objects might just drive straight to the second activity, skipping the folder-selection UI.

Also note that you may be creating several ChooserTarget objects, probably having each pointing to the same activity. You will need to ensure that the extras Bundle contains what you need to distinguish one request from the next. However, do not put custom Parcelable objects in this Bundle, as Android will attempt to un-parcel them as part of its work, and it will fail to do so since Android does not have your custom Parcelable class.

An Icon is a new construct in Android 6.0, serving as a wrapper around multiple possible image sources. You can create an Icon from a drawable resource (as the sample app does), from a Bitmap, from a byte array representing PNG or JPEG data, from a file path pointing to a PNG or JPEG file, or from a Uri to a ContentProvider pointing to an image.

The Manifest Entries

Your ChooserTargetService will have a typical <service> manifest entry, with two special bits:

    <service
      android:name=".CTService"
      android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
      <intent-filter>
        <action android:name="android.service.chooser.ChooserTargetService"/>
      </intent-filter>
    </service>

Your ACTION_SEND activity will have its normal <activity> element, with just one change: a <meta-data> element pointing to your ChooserTargetService:

    <activity
      android:name="FauxSender"
      android:label="@string/app_name"
      android:theme="@android:style/Theme.Translucent.NoTitleBar">
      <intent-filter android:label="@string/app_name">
        <action android:name="android.intent.action.SEND"/>

        <data android:mimeType="text/plain"/>

        <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
      <meta-data
        android:name="android.service.chooser.chooser_target_service"
        android:value=".CTService"/>
    </activity>

It is possible that your app has multiple ACTION_SEND activities. In that case, each could have its own ChooserTargetService. However, you could elect to have all of your ACTION_SEND activities route to the same ChooserTargetService if you prefer. onGetChooserTargets() is passed two parameters to help identify where the direct-share request is coming from:

Note that you are not given the content itself, in the form of the Intent that will eventually be delivered to your ACTION_SEND activity or to your direct-share target via its ComponentName. This is for privacy reasons; otherwise, an app could ask to share anything and be able to peek at anything the user tried sharing with any app.

The Results

The FauxSender activity — the one handling the ACTION_SEND Intent and the direct-share Intent — now looks for the EXTRA_TARGET_ID that the CTService put in its Intent and includes it in the Toast:

package com.commonsware.android.fsendermnc;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Toast;

public class FauxSender extends Activity {
  public static final String EXTRA_TARGET_ID="targetId";

  @Override
  public void onCreate(Bundle savedInstanceState) {
    String epilogue="";

    super.onCreate(savedInstanceState);

    int targetId=getIntent().getIntExtra(EXTRA_TARGET_ID, -1);

    if (targetId>0) {
      epilogue=" for target ID #"+targetId;
    }

    String msg=getIntent().getStringExtra(Intent.EXTRA_TEXT);

    if (TextUtils.isEmpty(msg)) {
      msg=getIntent().getStringExtra(Intent.EXTRA_SUBJECT);
    }

    if (TextUtils.isEmpty(msg)) {
      msg=getString(R.string.no_message_supplied);
    }

    Toast.makeText(this, msg+epilogue, Toast.LENGTH_LONG).show();

    finish();
  }
}

If you run the sample app from Android Studio, the launcher activity will trigger an ACTION_SEND of some text. That, in turn, will bring up the chooser panel… but on an Android 6.0 device, that panel will start off with our six direct-share targets:

Chooser, Showing Direct-Share Targets
Figure 889: Chooser, Showing Direct-Share Targets

Expanding the panel shows that our original ACTION_SEND activity is also there, after the direct-share targets:

Chooser, Showing More Share Targets
Figure 890: Chooser, Showing More Share Targets

If the user taps on the regular ACTION_SEND activity icon, the sample works as it did originally, showing a Toast with the text supplied by the launcher activity. If, however, the user taps on one of the direct-share targets, the Toast also shows which target was chosen:

Toast from a Direct-Share Target
Figure 891: Toast from a Direct-Share Target

Now, our Bundle for the direct-share target did not include the shared text, because we did not have it. Instead, the regular ACTION_SEND extras are merged in with our own extras, so our activity gets all of the relevant extras.

But… I Got Nothin’!

If you do not have any direct-share targets for a particular request, returning an empty list is perfectly fine.

If you know in advance that you will not have any direct-share targets — for example, the user has not really worked with your app yet after installation — you can disable the service (android:enabled="false"). Even though the <meta-data> will point to the service, the framework seems to detect the disabled service and continues on unabated.

Even if you elect to leave the service enabled at the outset for Android 6.0, you should consider disabling the service for earlier versions of Android, since it is useless on those devices. You could do this using boolean resources:

Best Practices

At the moment, it appears that Android 6.0 is limiting the number of share targets, only showing 8 of them. If you provide more than 8, Android will choose the ones with the highest score.

Since returning the list of direct-share targets should be involving IPC, there may be capacity limitations, for the number and size of the direct-share targets. Do not be surprised if you get a “FAILED BINDER TRANSACTION” exception if your roster of direct-share targets exceeds 1MB.

Hence, between those two limitations, you will want to constrain how many share targets you try returning from your ChooserTargetService.

As with other places in Android 5.0+ (e.g., large icons in notifications), your app’s icon will be applied as a badge over the icons that you use for direct-share targets. Make sure that your app’s icon will work both as a launcher icon and as a direct-share target badge.

Take the Shortcut

The preview of this section is presently indisposed.

Homing Beacons for Intents

The preview of this section is en route to Mars.

Integrating with Text Selection

The preview of this section is being chased by zombies.

Quick Settings and TileService

The preview of this section is off trying to sweet-talk the Khaleesi into providing us with a dragon.

Installing Packages

The preview of this section is out seeking fame and fortune as the Dread Pirate Roberts.

Deleting Packages

The preview of this section left for Hollywood to appear in a reality TV show.

Detecting Changes in Packages

The preview of this section is unavailable right now, but if you leave your name and number at the sound of the tone, it might get back to you (BEEEEEEEEEEEEP!).