androidengineers.Book a session

Android Platform Services

Sharesheet & Content Sharing

article20 minMedium

Android's share sheet lets your app send content to other apps, and receive content from them. Custom share targets and Direct Share let you surface frequently-used contacts directly in the sheet.

Sending Content (Sharing Out)

// Share plain text
fun shareText(context: Context, text: String, title: String? = null) {
    val intent = Intent(Intent.ACTION_SEND).apply {
        type = "text/plain"
        putExtra(Intent.EXTRA_TEXT, text)
        title?.let { putExtra(Intent.EXTRA_TITLE, it) }
    }
    context.startActivity(Intent.createChooser(intent, "Share via"))
}

// Share an image (URI — never a file path)
fun shareImage(context: Context, imageUri: Uri, message: String? = null) {
    val intent = Intent(Intent.ACTION_SEND).apply {
        type = "image/*"
        putExtra(Intent.EXTRA_STREAM, imageUri)
        message?.let { putExtra(Intent.EXTRA_TEXT, it) }
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
    context.startActivity(Intent.createChooser(intent, "Share image"))
}

// Share multiple images
fun shareMultipleImages(context: Context, uris: List<Uri>) {
    val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
        type = "image/*"
        putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris))
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
    context.startActivity(Intent.createChooser(intent, "Share images"))
}

// Share from Compose
@Composable
fun ShareButton(text: String) {
    val context = LocalContext.current
    IconButton(onClick = {
        shareText(context, text)
    }) {
        Icon(Icons.Default.Share, contentDescription = "Share")
    }
}

FileProvider: Safe URI Sharing

Never share file:// URIs — they're blocked since Android 7.0. Use FileProvider:

<!-- AndroidManifest.xml -->
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>
<!-- res/xml/file_paths.xml -->
<paths>
    <cache-path name="shared_images" path="share/" />
    <files-path name="documents" path="documents/" />
</paths>
fun getShareUri(context: Context, file: File): Uri {
    return FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
}

Receiving Content (Share Targets)

// In AndroidManifest.xml — declare your activity as a share target
// (in the activity element)
<intent-filter>
    <action android:name="android.intent.action.SEND" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
    <action android:name="android.intent.action.SEND" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="image/*" />
</intent-filter>
// Handle incoming share in Activity
class ShareReceiverActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handleIncomingShare()
    }

    private fun handleIncomingShare() {
        when (intent?.action) {
            Intent.ACTION_SEND -> {
                if (intent.type == "text/plain") {
                    val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: return
                    // Handle text
                } else if (intent.type?.startsWith("image/") == true) {
                    val imageUri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM) ?: return
                    // Handle image
                }
            }
            Intent.ACTION_SEND_MULTIPLE -> {
                val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM) ?: return
                // Handle multiple
            }
        }
    }
}

Direct Share (API 29+)

// Publish share targets for contacts — these appear in the sharesheet
class DirectShareService : ChooserTargetService() {
    // ChooserTargetService is deprecated; use ShortcutInfo for API 29+
}

// Preferred: ShortcutInfo-based direct share
fun publishShareTargets(context: Context, conversations: List<Conversation>) {
    val shortcutManager = context.getSystemService(ShortcutManager::class.java)

    val shortcuts = conversations.take(4).map { conv ->
        ShortcutInfo.Builder(context, conv.id)
            .setShortLabel(conv.name)
            .setIcon(Icon.createWithBitmap(conv.avatar))
            .setIntent(Intent(context, ShareReceiverActivity::class.java).apply {
                action = Intent.ACTION_DEFAULT
                putExtra("conversation_id", conv.id)
            })
            .setCategories(setOf("com.myapp.category.SHARE_TARGET"))
            .setLongLived(true)
            .build()
    }

    shortcutManager.pushDynamicShortcut(shortcuts.first())
}

Key Takeaways

PatternRule
ACTION_SENDSingle file/text share; ACTION_SEND_MULTIPLE for batch
FileProviderAlways use for file://content:// conversion; never share raw file paths
FLAG_GRANT_READ_URI_PERMISSIONRequired when sharing URIs to other apps
Share target intent-filterDeclare MIME types your app can receive
ShortcutInfo for Direct ShareAPI 29+; use dynamic shortcuts with a share category

YOUR LEARNING JOURNEY

0 of 177 available lessons completed

Progress saved in this browser. No account needed.
Sharesheet & Content Sharing | Android System Design | Android Engineers