How to Use Dynamic Image Templates to Share Android App Content on Social Media
Render app data into a shareable image, expose it securely with FileProvider, and open Android’s Sharesheet for social apps.
Short answer: render your app data into an image, save it in app storage, expose that file through a FileProvider content URI, then send the URI with an ACTION_SEND intent. Wrap the intent in Intent.createChooser() so Android displays the Sharesheet and the user chooses the destination.
This approach shares an image through Android. It does not guarantee how a particular social app will crop the image, publish a caption, or handle a thumbnail. Test the destinations and Android versions you support.
1. Choose the template output before writing code
A dynamic template is an app-side rendering step. Combine values such as a title, statistic, avatar, chart, or user-selected photo with a fixed layout, then export the result as PNG, JPEG, or another format accepted by your target apps.
Design checklist
- Pick an aspect ratio for the destinations you care about and keep important text away from edges that may be cropped.
- Use a readable type size after the image is resized by a social app.
- Balance quality and file size. Android’s media-sharing guidance mentions WebP and AVIF as compression options; verify that every intended receiver accepts your chosen format.
- Let the user review the rendered image before opening the Sharesheet.
- Keep captions and links as separate intent data. A receiver may ignore, truncate, or transform them.
2. Add a secure FileProvider
Another app cannot reliably open a private filesystem path. Expose the rendered file as a content:// URI and grant temporary read access. Android documents FileProvider and MediaStore as supported ways to provide shared file data.
Manifest declaration
<application
...>
<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>
</application>
Shareable path configuration
Create app/src/main/res/xml/file_paths.xml. The example grants access only to a dedicated subdirectory inside your app’s cache directory.
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path
name="shared_images"
path="shared/" />
</paths>
Keep the path narrow. Do not expose your entire data directory when a single temporary folder is enough.
3. Render a dynamic image template in Kotlin
The following example creates a 1200×630 bitmap, draws app data onto it, writes a JPEG into the configured cache directory, and returns the file.
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Typeface
import java.io.File
import java.io.FileOutputStream
fun renderShareImage(
context: Context,
title: String,
value: String,
subtitle: String
): File {
val width = 1200
val height = 630
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawColor(Color.rgb(248, 249, 252))
val titlePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.rgb(25, 30, 45)
textSize = 64f
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
}
val valuePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.rgb(40, 90, 220)
textSize = 150f
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
}
val subtitlePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.rgb(90, 96, 110)
textSize = 38f
}
canvas.drawText(title, 72f, 150f, titlePaint)
canvas.drawText(value, 72f, 370f, valuePaint)
canvas.drawText(subtitle, 72f, 470f, subtitlePaint)
val directory = File(context.cacheDir, "shared").apply { mkdirs() }
val output = File(directory, "share-${System.currentTimeMillis()}.jpg")
FileOutputStream(output).use { stream ->
bitmap.compress(Bitmap.CompressFormat.JPEG, 92, stream)
}
bitmap.recycle()
return output
}
For a production template, measure text before drawing, wrap long strings, load images off the main thread, and handle missing user data. If transparency matters, use PNG or a format and MIME type that the receiving apps support.
4. Share one image through the Android Sharesheet
Use the actual output MIME type. A JPEG should use image/jpeg; a PNG should use image/png. The receiving activity uses this type when deciding whether it can accept the share.
import android.content.Context
import android.content.Intent
import androidx.core.content.FileProvider
import java.io.File
fun shareImage(context: Context, imageFile: File) {
val authority = "${context.packageName}.fileprovider"
val contentUri = FileProvider.getUriForFile(
context,
authority,
imageFile
)
val sendIntent = Intent(Intent.ACTION_SEND).apply {
type = "image/jpeg"
putExtra(Intent.EXTRA_STREAM, contentUri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
val chooser = Intent.createChooser(sendIntent, null)
context.startActivity(chooser)
}
Intent.createChooser() displays the Android Sharesheet, where the user selects an installed compatible app. The temporary URI grant allows that receiver to read the image during the handoff and expires automatically.
Share a caption or link
val sendIntent = Intent(Intent.ACTION_SEND).apply {
type = "image/jpeg"
putExtra(Intent.EXTRA_STREAM, contentUri)
putExtra(Intent.EXTRA_TEXT, "My latest result: https://example.com/result")
putExtra(Intent.EXTRA_TITLE, "Latest result")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
Text support varies by receiver. Android 10 (API 29) added richer text-preview behavior for chooser content, including optional title and thumbnail metadata, but the destination still decides what it publishes.
5. Share multiple rendered images
Use ACTION_SEND_MULTIPLE only when the user intentionally shares more than one image.
val uris = imageFiles.map { file ->
FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
file
)
}
val sendIntent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
type = "image/jpeg"
putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris))
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(Intent.createChooser(sendIntent, null))
All files must remain readable for the duration of the handoff. Clean old temporary files later rather than deleting them before the receiving app opens them.
6. Optional chooser enhancements by Android version
| Capability | Minimum Android version | Use it when |
|---|---|---|
| Basic image Sharesheet | All supported versions | You need the standard user-selected handoff. |
| Text preview behavior | Android 10, API 29 | You include a caption or link and want richer chooser context. |
| Custom chooser actions | Android 14, API 34 | You have an optional action that improves the chooser experience. |
Keep these enhancements optional. The image URI and chooser flow are the baseline implementation.
7. Test the complete flow
- Render an image with short and long values, missing optional fields, and non-ASCII text.
- Confirm the file exists and opens before starting the intent.
- Inspect the URI: it should be a
content://URI from yourFileProvider, not a raw private path. - Test JPEG and PNG receivers on the Android versions you support.
- Try at least two actual destination apps and verify whether the image, caption, link, and crop are handled as expected.
- Test with no compatible receiver installed and provide a clear fallback message.
- Test rotation, process recreation, and background-to-foreground transitions so the rendered file and pending share state are not lost.
8. Troubleshooting common errors
| Symptom | Likely cause | Fix |
|---|---|---|
IllegalArgumentException: Failed to find configured root |
The file is outside every path declared in file_paths.xml. |
Write into the configured cache/shared/ directory or add a narrowly scoped matching path. |
| The receiver says the attachment is empty | The file was deleted too early, or the URI grant was missing. | Keep the file until the handoff completes and add FLAG_GRANT_READ_URI_PERMISSION. |
| No apps appear in the Sharesheet | The MIME type does not match the file or installed receivers. | Use the exact type such as image/jpeg and test with a known image receiver. |
| Sharing works in one app but not another | Destination apps implement intent handling differently. | Test each intended destination and document supported behavior; Android cannot guarantee third-party handling. |
| Caption is missing | The receiving app ignores EXTRA_TEXT or treats image and text separately. |
Keep the caption optional and verify the destination’s current behavior. |
| Text is clipped in the image | The template assumes a fixed string length. | Measure and wrap text, reduce size when needed, and test localized strings. |
| Image looks blurry after posting | The destination resized or recompressed it. | Render at a suitable resolution, use a supported format, and avoid unnecessary intermediate compression. |
| Share fails after process death | The app kept the file path only in memory. | Persist enough state to recreate the file or URI and handle the activity result lifecycle. |
9. Performance, reliability, and privacy
Performance
- Render and compress on a background dispatcher; never block the main thread with bitmap work.
- Reuse decoded assets and cap bitmap dimensions to the output size to avoid memory spikes.
- Generate only after the user requests sharing, unless a preview screen needs the image earlier.
- Prefer one final compression pass and measure the resulting file size on representative devices.
Reliability
- Check storage and compression results before launching the chooser.
- Use unique filenames to prevent concurrent shares from overwriting one another.
- Retain temporary files long enough for slow receivers, then clean them with a scheduled policy.
- Do not promise that every social network accepts every format, caption, ratio, or number of images.
Privacy
- Render only the data the user intends to share.
- Review personal information in the preview and avoid putting secrets into filenames or captions.
- Grant read access only to the selected content URI and only for the temporary handoff.
10. Or skip the browser setup
If the image you need is a web-rendered preview, dashboard, or hosted template, ScreenshotNeo can capture it with one request. Its cleanup step accepts consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response reports the verdict and billing status in headers.
See the ScreenshotNeo API documentation for all options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
You can also use its MCP server with Claude, Cursor, or another MCP client through take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
11. FAQ
Should I use FileProvider or MediaStore?
Use the provider that fits your storage design. FileProvider is convenient for a temporary app-generated file; MediaStore can fit media that belongs in shared device storage.
Can Android publish directly to every social network?
No. Android hands the content to a selected app. Each destination controls its own upload, caption, crop, and format behavior.
Do I need ACTION_SEND_MULTIPLE for one template?
No. Use ACTION_SEND for one image and ACTION_SEND_MULTIPLE for a deliberate multi-image share.
Is a file URI safe to send?
Use a content URI from FileProvider or another supported provider, grant temporary read permission, and avoid exposing raw private filesystem paths.
Can I add a custom button to the Sharesheet?
Android 14 (API 34) documents custom chooser actions. Treat them as optional enhancements and retain the normal chooser behavior for older versions.
12. Final implementation checklist
- Template output matches the aspect ratios and formats your destinations support.
- Image rendering happens off the main thread.
- Files are written under a narrowly scoped FileProvider path.
- The share uses a content URI, exact MIME type, and temporary read permission.
Intent.createChooser()opens the system Sharesheet.- Captions and links are treated as optional destination-dependent data.
- Long text, localization, missing data, process death, and no-target cases are handled.
- Real destination apps are tested before promising a specific publishing result.
With this flow, your app owns the visual template and Android owns the secure handoff. The user remains in control of the destination and can review the final image before sharing.


