Android Share: Integrate Your App Into The Share Sheet
Hey guys! Ever wondered how to make your Android app appear in the system Share sheet? You know, so users can share a page from Chrome or any browser directly into your app? It’s pretty cool, and I’m here to break it down for you in simple terms. Chrome usually shares the page URL as plain text, sometimes even with the title, which is super handy.
This guide will walk you through a minimal, working setup in Kotlin. We'll cover everything from declaring a Share target in your AndroidManifest.xml
to handling the incoming share in your ShareReceiverActivity.kt
. Let's dive in!
1. Declare a Share Target (AndroidManifest.xml)
First things first, you need to tell Android that your app can handle shared content. This is done by declaring a Share target in your AndroidManifest.xml
file. Think of this as registering your app as a valid option in the Share sheet. This is a crucial step because without it, your app won't even show up when someone tries to share something.
<application ...>
<activity
android:name=".ShareReceiverActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<!-- Receive URLs or arbitrary shared text -->
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<!-- Optional: receive a shared Uri (e.g., text/uri-list) -->
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/uri-list" />
</intent-filter>
</activity>
</application>
Let’s break down this XML snippet. The <activity>
tag declares your ShareReceiverActivity
, which will handle the incoming shared content. The android:exported="true"
attribute is super important because it allows other applications to start this activity. Without this, your activity won't be accessible from the Share sheet.
Inside the <activity>
tag, you'll find <intent-filter>
elements. These filters specify what types of intents your activity can handle. In this case, we're interested in android.intent.action.SEND
, which is the action used when sharing content. The <category android:name="android.intent.category.DEFAULT" />
line ensures that your activity is a default handler for this action.
The <data android:mimeType="text/plain" />
line specifies that your activity can handle plain text data, which is the most common type of content shared from browsers. We also include an optional <intent-filter>
to handle text/uri-list
, which is used when sharing a list of URIs. This covers most bases, ensuring your app can handle a variety of shared content.
Why is this important? Declaring the Share target correctly is the foundation of making your app accessible via the Share sheet. Without it, no matter how well you handle the incoming data, your app simply won’t be an option for users.
2. Handle the Incoming Share (ShareReceiverActivity.kt)
Now that you've declared your app as a Share target, the next step is to actually handle the shared content. This is where the ShareReceiverActivity.kt
file comes into play. This activity will be launched when a user selects your app from the Share sheet. Your job here is to extract the relevant data from the intent and do something with it. Let's take a look at the code:
package com.example.sharetarget
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.util.Patterns
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
class ShareReceiverActivity : Activity() {
private val client by lazy { OkHttpClient() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (intent?.action == Intent.ACTION_SEND) {
when (intent.type) {
"text/plain" -> handleText(intent.getStringExtra(Intent.EXTRA_TEXT), intent.getStringExtra(Intent.EXTRA_SUBJECT))
"text/uri-list" -> handleText(intent.getStringExtra(Intent.EXTRA_TEXT), intent.getStringExtra(Intent.EXTRA_SUBJECT))
else -> finish() // unsupported
}
} else {
finish()
}
}
private fun handleText(sharedText: String?, subject: String?) {
if (sharedText.isNullOrBlank()) { finish(); return }
// Chrome usually puts the URL in EXTRA_TEXT. Sometimes it includes extra text; extract the first URL.
val url = Patterns.WEB_URL.matcher(sharedText).takeIf { it.find() }?.group()
if (url == null) { finish(); return }
val title = subject ?: "" // Chrome often sets subject to page title
// Fire-and-forget to your backend, then close the share sheet quickly
lifecycleScope.launch {
postToBackend(url, title)
finish() // close promptly; Share targets should be fast
}
}
private suspend fun postToBackend(url: String, title: String) = withContext(Dispatchers.IO) {
val json = JSONObject()
.put("url", url)
.put("title", title)
.put("ts", System.currentTimeMillis())
.toString()
val req = Request.Builder()
.url("https://YOUR-DOMAIN.example/collect")
.post(json.toRequestBody("application/json".toMediaType()))
.build()
runCatching { client.newCall(req).execute().close() }
}
}
Okay, there's a lot going on here, so let's break it down piece by piece. First, we have the ShareReceiverActivity
class, which extends Activity
. This is where the magic happens.
In the onCreate
method, we check if the intent's action is Intent.ACTION_SEND
. This confirms that we're dealing with a share intent. Then, we use a when
statement to handle different MIME types. We're primarily interested in text/plain
and text/uri-list
, which cover most shared URLs. If the intent type is something else, we simply finish()
the activity, as we don't support it.
The handleText
function is where we extract the URL and title from the shared text. Chrome usually puts the URL in EXTRA_TEXT
, and sometimes includes extra text. We use Patterns.WEB_URL.matcher
to find the first URL in the shared text. The subject, which often contains the page title, is retrieved from Intent.EXTRA_SUBJECT
. If no subject is provided, we default to an empty string.
The postToBackend
function is a suspend
function that sends the URL and title to your backend. We use OkHttp
to make a POST request to a specified endpoint. The data is sent as a JSON object containing the URL, title, and a timestamp. We use runCatching
to handle any potential exceptions during the network request.
Key Takeaways:
- Intent Handling: The
onCreate
method is the entry point for handling the shared content. It checks the intent action and type to determine how to process the data. - URL Extraction: The
handleText
function extracts the URL from the shared text using regular expressions. This is crucial for getting the actual link that the user wants to share. - Asynchronous Backend Post: The
postToBackend
function uses Kotlin coroutines to send the data to your backend asynchronously. This ensures that your UI remains responsive and the share sheet closes quickly. - Error Handling: The
runCatching
block inpostToBackend
handles potential network errors, making your app more robust.
3. Dependencies (in build.gradle)
To make the above code work, you need to add a couple of dependencies to your build.gradle
file. These dependencies provide the necessary libraries for making HTTP requests and using Kotlin coroutines:
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1")
}
- OkHttp: This is a powerful HTTP client for making network requests. We use it in the
postToBackend
function to send the shared URL and title to your server. - Kotlin Coroutines: These provide a way to write asynchronous code in a sequential style. We use them to perform the backend post in a non-blocking manner, ensuring that the UI remains responsive.
Don't forget to sync your Gradle files after adding these dependencies!
4. How It Works (UX)
Let’s walk through the user experience to see how all these pieces fit together:
- User opens a webpage in Chrome: The user finds a page they want to share.
- Taps Share: The user taps the Share button in Chrome (or any other browser).
- Picks Your App: The Android Share sheet appears, and the user selects your app from the list of available options.
- Android starts your ShareReceiverActivity with ACTION_SEND: Android launches your
ShareReceiverActivity
to handle the shared content. - You extract the URL from EXTRA_TEXT and send it to your backend: Your activity extracts the URL and title from the intent and sends it to your server for processing.
- Activity finishes quickly: The
ShareReceiverActivity
finishes promptly, providing a smooth user experience. Share targets should be fast to avoid frustrating the user.
The key here is speed and efficiency. Users expect the Share sheet to be quick and responsive. By handling the share in the background and finishing the activity promptly, you provide a seamless experience.
Notes & Options
Here are some additional notes and options to consider when implementing Android sharing in your app:
- No special permissions needed for plain text URLs: Sharing plain text URLs doesn't require any special permissions, making it a straightforward process.
- Some apps share a Uri (text/uri-list): As mentioned earlier, the code provided tolerates this path as well, ensuring your app can handle different types of shared content.
- Nicer UI (tags/notes): If you want to provide a richer sharing experience, you can show a minimal screen to collect additional input, such as tags or notes, before posting the data. This allows users to add context to their shared content.
- Share Shortcuts: For repeat users, you can publish a Share Shortcut via
ShortcutManager
. This pins your app near the top of the Share sheet, making it even easier for users to share content to your app. This is a great way to improve user engagement. - PWA Web Share Target: If you're interested in a cross-platform solution, a PWA Web Share Target can also receive shared URLs on Android. This allows you to handle sharing across both web and native platforms.
Tiny Sample Project
If you're the type who likes to learn by doing, I can whip up a tiny sample project (Gradle + manifest + activity) that you can drop into Android Studio and run. Let me know if you'd like that, and I'll get it sorted for you!
Conclusion
So, there you have it! Making your Android app appear in the system Share sheet is a fantastic way to boost user engagement and make your app more accessible. By following the steps outlined in this guide, you can easily implement this feature in your own apps. Remember to focus on providing a fast and seamless sharing experience for your users.