Implementing the New Approach

In effect, Android 10 allows dynamic shortcuts to serve as share targets, in lieu of using a ChooserTargetService.

However, it will be years before Android 10 and higher devices become dominant. Google is making available an AndroidX library that uses the new Android 10 solution on compatible devices and falls back to ChooserTargetService for older ones.

The ShareTargets sample module in the book’s sample project demonstrates how this works.

Add the Dependency

The AndroidX dependency that lets Android 10 share targets work on older devices is androidx.sharetarget:

  implementation "androidx.sharetarget:sharetarget:$sharetarget_version"
  sharetarget_version = "1.0.0-beta02"
(from build.gradle)

This library provides a ChooserTargetService implementation called androidx.sharetarget.ChooserTargetServiceCompat. We get its <service> manifest element automatically through the manifest merger process. However, we have to add an android.service.chooser.chooser_target_service <meta-data> element to the <activity> element that represents our ACTION_SEND implementation, where the <meta-data> points to this supplied service:

    <activity android:name=".ShareActivity">
      <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="*/*" />
      </intent-filter>
      <meta-data
        android:name="android.service.chooser.chooser_target_service"
        android:value="androidx.sharetarget.ChooserTargetServiceCompat" />
    </activity>

Declare Share Targets

We then need to have an XML resource (res/xml/) that contains our share targets. For projects that already use static shortcuts, you can add a <share-target> element to your existing static shortcuts resource. Otherwise, you will need to create one.

So, our sample module has a res/xml/share_targets.xml resource to fulfill this requirement:

<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
  <share-target android:targetClass="com.commonsware.android.q.sharetargets.ShareActivity">
    <data android:mimeType="*/*" />
    <category android:name="com.commonsware.android.q.sharetargets.CUSTOM_SHARE_TARGET" />
  </share-target>
</shortcuts>

Your <share-target> element will need an android:targetClass attribute, containing the fully-qualified class name of the ACTION_SEND activity. You also need:

In this case, the category is not an <intent-filter> category. Rather, it is simply an identifier that we will use to connect this <share-target> element with some corresponding dynamic shortcuts.

You can have as many <share-target> elements as needed, though a typical app will not need more than one ACTION_SEND activity. An individual <share-target> element can have as many <data> and <category> elements as needed, and in this case, more than one <data> element may be needed to match the desired roster of MIME types.

As with regular static shortcuts, this XML resource needs to be identified by a android.app.shortcuts <meta-data> element on the LAUNCHER <activity> element:

    <activity
      android:name=".MainActivity"
      android:theme="@android:style/Theme.Translucent.NoTitleBar">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <intent-filter>
        <action android:name="${applicationId}.ACTION_WHATEVER" />

        <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>

      <meta-data
        android:name="android.app.shortcuts"
        android:resource="@xml/share_targets" />
    </activity>

(we will discuss that odd second <intent-filter> shortly)

Register Dynamic Shortcuts

We then need to register dynamic shortcuts using ShortcutManager (or the AndroidX ShortcutManagerCompat). These represent the actual entries that should show up in the “share sheet” UI. And, in particular, they need to have a category that matches a category used in a <share-target> element from the shortcuts XML resource.

The app’s MainActivity will create those dynamic shortcuts in onCreate(), if they have not already been registered:

package com.commonsware.android.q.sharetargets

import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat

data class ShareTarget(
  val id: String,
  @StringRes val shortLabelRes: Int,
  @DrawableRes val iconRes: Int
)

private val SHARE_CATEGORIES =
  setOf("com.commonsware.android.q.sharetargets.CUSTOM_SHARE_TARGET")
private val TARGETS = listOf(
  ShareTarget("one", R.string.tag_one, R.drawable.ic_looks_one_black_24dp),
  ShareTarget("two", R.string.tag_two, R.drawable.ic_looks_two_black_24dp),
  ShareTarget("five", R.string.tag_five, R.drawable.ic_looks_5_black_24dp)
)

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    if (ShortcutManagerCompat.getDynamicShortcuts(this).size == 0) {
      val intent = Intent("$packageName.ACTION_WHATEVER")

      ShortcutManagerCompat.addDynamicShortcuts(this, TARGETS.map { tag ->
        ShortcutInfoCompat.Builder(this, tag.id)
          .setShortLabel(getString(tag.shortLabelRes))
          .setIcon(IconCompat.createWithResource(this, tag.iconRes))
          .setIntent(intent)
          .setLongLived(true)
          .setCategories(SHARE_CATEGORIES)
          .build()
      })

      Toast.makeText(this, "Share targets ready!", Toast.LENGTH_LONG).show()
    } else {
      Toast.makeText(this, "${intent.action} received!", Toast.LENGTH_LONG).show()
    }

    finish()
  }
}

Here, we iterate over a TARGETS list of ShareTarget objects. Those simply aggregate some metadata that we need for the dynamic shortcuts: an ID, an icon, and a label. We convert those to ShortcutInfoCompat objects via ShortcutInfoCompat.Builder. Each of those ShortcutInfoCompat objects also gets:


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.