Home · Android & Kotlin Tutorials

Sharing in Android 10: Getting Started

In this Sharing in Android 10 tutorial, you’ll learn how to use the Sharing Shortcuts API to receive and share images in an app.

5/5 3 Ratings

Version

  • Kotlin 1.3, Android 10.0, Android Studio 3.5

These days, mobile users don’t just want to consume interesting information, they want to share it with their friends as well. Now that many people have internet wherever they go, the need to send and receive text, images and other files from mobile apps has become extremely important.

In this Android 10 tutorial, you’ll learn the best way to share files simply and efficiently, allowing you to add this crucial task to your own apps.

You’ll do this by improving Meme Repo, an app that lets you curate your own repository of funny images, so that you can use the app to easily exchange data.

Along the way, you’ll learn how to:

  • Receive a shared image from another app.
  • Quick share with sharing shortcuts.
  • Share images from your app.

Getting Started

Use the Download Materials button at the top or bottom of this tutorial to download the begin project.

You’ll notice that the entire app is already there. Here’s a quick breakdown of the code, to show you around a bit:

Structure of the begin project

  • MainActivity.kt: Your central activity that displays a grid of meme images organized into tabbed categories. The code that supports this UI lives in MemeCollectionFragment.kt, MemesAdapter.kt and SectionsPagerAdapter.kt.
  • MemeActivity.kt: Allows you to add a new meme to your collection by supplying its image URL, title and category.
  • Meme.kt: The meme’s model class.
  • Category.kt: An enum class that defines a meme category. Currently, the app supports Classic and Dank memes.
  • MemeRepo.kt: A singleton that manages your collection, caching it and making sure it reads from and writes to local preferences.

Build and run. Most of the app already works nicely:

Meme Repo's start screen

You can already add a meme:

Meme Repo with an added meme

However, you’ll soon notice that the app isn’t very easy to use. For example, if you run into an interesting meme while browsing the internet, you have to copy its image link, go to the app, press the + button and then paste the URL into the field.

If that sounds like a hassle. Android sharing is here to save you.

Receiving a Meme From Another App

In order to build your repository, you need a convenient way to send the memes you find while using other apps to Meme Repo. Your first step will be to improve the app by allowing other apps to share images with it.

Open AndroidManifest.xml and add this code inside activity android:name=".activity.MemeActivity"...:

<intent-filter>
  //1
  <action android:name="android.intent.action.SEND" /> 
  //2
  <category android:name="android.intent.category.DEFAULT" />
  //3  
  <data android:mimeType="image/*" />
</intent-filter>

This code adds a new intent-filter that allows other apps to share with Meme Repo:

  1. It sets action to android.intent.action.SEND, which is the norm for sharing.
  2. The category is android.intent.category.DEFAULT.
  3. The shared content is an image. That is, its MIME type corresponds to image/*.

You’ve now exposed your app to other apps for sharing, but you still need to handle the incoming shared content.

Handling Incoming Content

Open MemeActivity.kt and add this at the end of onCreate:

//1
if (intent?.action == Intent.ACTION_SEND) { 
  //2
  if (intent.type?.startsWith("image/") == true) {
    //3
    (intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri)?.let {
       // 4
       memeUri.setText(it.toString()) 
    }
  }
  // TODO: More to come here.
}

This code:

  1. Checks if the Activity started with an intent equal to Intent.ACTION_SEND.
  2. Checks if the shared content is an image.
  3. Checks if the shared image is available in the intent extras.
  4. If the incoming content meets all the conditions, the app pastes the URL of the shared image into memeUri‘s text field.

Build and run.

Try sharing an image from another app to Meme Repo by opening your browser, navigating to an awesome web page and long-pressing an image to share it.

Long-press image menu

Meme Repo now appears as an option alongside some other apps, making it much simpler to build your meme collection.

Meme Repo appears among your sharing options

Using Sharing Shortcuts

Note: As of December 2019, the Android 10 emulator has a bug which prevents sharing shortcuts from working properly. When you try to use it, you’ll get the message: Direct Share not available in the share sheet.

Hopefully, Google will fix this soon. In the meantime, please use a real device to test the code from this section.

Now that you’ve eliminated the need to manually switch apps and paste an image URL, your next step is to make it easier for the user to select the meme’s category.

To do this, you’ll use the sharing shortcuts API. If you use any of the popular messaging apps, you’ve seen this API in use when the app offers you a few contacts to directly share content with, without having to manually specify the recipient.

In this case, you’ll add two shortcuts to the sharing screen so that the user can immediately decide if a meme is Classic or Dank. It takes only a few simple steps to do this, starting with creating a file where your shortcuts will live.

Creating a Shortcuts File

Start by adding a new file to res/xml and name it shortcuts.xml. Enter the following code as its content:

<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
  //1
  <share-target android:targetClass="com.raywenderlich.android.memerepo.activity.MemeActivity">
    //2
    <data android:mimeType="image/*" />
    //3
    <category android:name="com.raywenderlich.android.memerepo.category.MEME_STORE_TARGET" />
  </share-target>
</shortcuts>

With this code, you’re specifying that:

  1. The shortcuts will take the user to MemeActivity.
  2. Images are the only supported content for sharing.
  3. The category uniquely identifies your sharing shortcuts.

Now that you have a shortcuts file, you need to link it to your app.

Linking Your Shortcuts File to Meme Repo

For your next step, go to AndroidManifest.xml and add this code in <activity android:name=".activity.MainActivity" ..., just before intent-filter:

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

While you’re updating the manifest, add this code in <activity android:name=".activity.MemeActivity" ..., just before intent-filter:

<meta-data
  android:name="android.service.chooser.chooser_target_service"
  android:value="androidx.sharetarget.ChooserTargetServiceCompat" />

Here, you’re ensuring that your app is compatible with previous OS versions.

Organizing Your Sharing Code

Now, it’s time for the juicy part. To keep all the sharing-related code in one place, create a new singleton file in the util package and name it ShareUtil.kt. Put a single constant in it, equal to the category you previously listed in shortcuts.xml.

object ShareUtil {
 private const val SHARE_CATEGORY
     = "com.raywenderlich.android.memerepo.category.MEME_STORE_TARGET"
}

You’ll use this constant in the next function.

Add the following extension method to ShareUtil to transform a Category into a ShortcutInfoCompat:

private fun Category.toShortcut(context: Context): ShortcutInfoCompat {
  val label = context.getString(stringResId)
  //1
  return ShortcutInfoCompat.Builder(context, id) 
       //2
      .setShortLabel(label) 
       //3
      .setLongLabel(context.getString(R.string.new_meme, label)) 
       //4
      .setPerson(Person.Builder()
          .setName(name)
          .setKey(id)
          .build())
       //5
      .setIcon(IconCompat.createWithResource(context, imageResId)) 
       //6
      .setCategories(setOf(SHARE_CATEGORY)) 
       //7
      .setIntent(Intent(context, MemeActivity::class.java).apply { 
        action = Intent.ACTION_SEND
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
          //8
          putExtra(Intent.EXTRA_SHORTCUT_ID, id) 
        }
      })
       //9
      .setLongLived(true) 
       //10
      .setRank(0) 
      .build()
}

There’s a lot going on in that code, so here’s a quick breakdown:

  1. Each shortcut needs a unique ID.
  2. Short label is a user-facing text shown in the share sheet, among other places.
  3. Long label allows for more descriptive text.
  4. Associating a Person is optional, but it improves the shortcut’s ranking. A person in this case is one of two Category enums
  5. Using IconCompat ensures your shortcut icon looks like you intend it to.
  6. Each shortcut has a category. In this case, you declared the category in shortcuts.xml.
  7. This intent triggers when you use quick shortcuts on the app icon.
  8. Android Pie introduced Intent.EXTRA_SHORTCUT_ID. You’ll conditionally set this required key depending on the version of Android. Using this extra allows the receiver Activity to identify the sender category.
  9. The OS caches long-lived shortcuts after they’re unpublished. More on publishing/unpublishing in the next section.
  10. Rank dictates the order of published shortcuts. The lower the ranking, the better.

With the grunt of the work done, you are ready to actually publish the shortcuts.

Publishing Your Shortcuts

Before you can see your shortcuts while using other apps, you need to publish them. To do this, add this method to ShareUtil.kt:

fun publishMemeShareShortcuts(context: Context) {
  ShortcutManagerCompat.addDynamicShortcuts(context, Category.values()
      .take(ShortcutManagerCompat.getMaxShortcutCountPerActivity(context))
      .map { it.toShortcut(context) }
  )
}

Then, invoke it at the end of MainActivity.kt onCreate:

ShareUtil.publishMemeShareShortcuts(this)

Effectively, the above two snippets of code publish the list of shortcuts to the Android sharing system. If there are already dynamic or pinned shortcuts with the same IDs, each mutable shortcut is updated.

Build and run, then try to share the image again. This time, you’ll see the sharing shortcuts for your meme categories.

Sharing shortcuts with meme categories

The shortcuts work, but not quite like you intended. The category still defaults to Classic. You’ll fix that next.

Finishing Your Shortcuts

To make everything come together, open MemeActivity.kt and replace the TODO in onCreate with this code:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
  val id = intent.getStringExtra(Intent.EXTRA_SHORTCUT_ID) 
  if (id?.isNotEmpty() == true) { 
    category.setSelection(Category.positionFor(id))
  }
}

This code checks if the share intent contains a shortcut ID, which you set by navigating to MemeActivity via a sharing shortcut, and then selects the category.

Build and run. Now, sharing an image to your app via a sharing shortcut will properly share both the image URL and the selected category:

Shared image with URL and category

Sharing shortcuts also give you an extra advantage: Long-press the app icon and you’ll see that you sharing shortcuts appear there as well:

Sharing shortcuts in the long-press menu

Pull or Push?

If you’re an Android user, you might have noticed that after you try to share something, the share sheet takes a while to appear.

This happens because, prior to Android 10, all the sharing shortcuts were on-demand. You’d decide to share something and then the OS would prompt the apps to provide their list of shortcuts for the specific sharing target. Call this the pull model.

Android 10 Sharing Shortcuts API solves this issue by opting for a push model. Apps dynamically publish shortcuts at runtime, which means that the entire share sheet is ready as soon as you decide to share something. This eliminates any lag.

So how do these two conflicting models work together if you deploy your Android 10 app on a device running an older Android version? Well, AndroidX has your back: Using ShortcutInfoCompat and ShortcutManagerCompat assures that your sharing shortcuts code will work properly on all devices and OS versions.

Making Your App Stable

Creating the shortcuts dynamically means that you have to be more careful about some details. For example, you probably noticed this line in publishMemeShareShortcuts:

.take(ShortcutManagerCompat.getMaxShortcutCountPerActivity(context))

There’s a hard limit of four sharing shortcuts per Activity. The exact behavior varies between devices, but on some, publishing more than four sharing shortcuts will result in a crash.

To make sure you’re safe, add this method to ShareUtil:

fun unPublishMemeShareShortcuts(context: Context) {
  ShortcutManagerCompat.removeAllDynamicShortcuts(context)
}

Then, invoke it on onDestroy() in MainActivity:

override fun onDestroy() {
  super.onDestroy()
  ShareUtil.unPublishMemeShareShortcuts(this)
}

This prevents any duplicated sharing categories, and prevents too many sharing shortcuts from being displayed to the user.

Build and run, even though there’s nothing new to see, you can sleep peacefully at night knowing that your app won’t crash, even on older devices. :]

Sleeping peacefully now that your app is stable

With the ability to share to your app, in the next section, you’ll focus on sharing from your app to other apps.

Sharing a Meme From Your App

A meme collection is pointless if you can’t share it with your friends, right? Your next step will be to implement the capability to share memes directly from your app.

Start by updating ShareUtil.kt with this code:

//1
private const val FILE_PROVIDER= "com.raywenderlich.android.memerepo.FileProvider" 

fun shareMeme(context: Context, meme: Meme) {
  //2
  val intent = Intent(Intent.ACTION_SEND).apply { 
    //3
    type = "image/jpeg"
    //4
    putExtra(Intent.EXTRA_TITLE, meme.title)
    //5
    val uri = Uri.parse(meme.url) 
    putExtra(Intent.EXTRA_STREAM, uri)
    //6
    clipData = ClipData.newUri(context.contentResolver, context.getString(R.string.app_name),FileProvider.getUriForFile(context, FILE_PROVIDER, File(meme.url)))
    //7
    flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
  }
  //8
  context.startActivity(Intent.createChooser(intent, null)) 
}

Here’s a step-by-step walk-through of this code:

  1. The app’s file provider name, which is the same as the one you used in AndroidManifest.xml.
  2. Sharing requires a custom intent whose action must be Intent.ACTION_SEND.
  3. MIME type is always image/jpeg because the repo saves all the memes locally as JPEGs.
  4. Sets the share sheet title.
  5. Resolves the meme’s local URL and adds it to the intent as Intent.EXTRA_STREAM.
  6. ClipData allows the share sheet to show a snippet of the shared content.
  7. You need this flag to let the intent read local data and stream the image content to another app.
  8. Starts the share sheet.

Now, you’ve set up the ability to share your memes, but tapping on an image still does nothing. You still need to wire everything up.

Sharing When You Tap Images

The final step is to invoke sharing when you tap a meme. Open MemesAdapter.kt and update the class ViewHolder on the bind with the following:

view.setOnClickListener {
  ShareUtil.shareMeme(view.context, meme)
}

Build and run one final time. Tap on any meme in your collection and the share sheet appears.

Sharing images with Meme Repo

Wasn’t that easy? Sharing really is caring!

Where to Go From Here?

Congratulations! On top of learning how to share and receive data between apps, you created a nice Meme Repo app that you can use every day!

You can download the final version of this project using the Download Materials button at the top or bottom of this tutorial.

Learn more details about Android 10 sharing from the official Android guides.

The Meme Repo app relies on GridView to render the meme collection. Learn more about using this component in our Android GridView Tutorial.

You can also learn more about intents in general from our Android Intents Tutorial with Kotlin.

We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!

Average Rating

5/5

Add a rating for this content

3 ratings

More like this

Contributors

Comments