My App manages a few videos and photos downloaded from a IoT device. It downloads videos & photos from these devices and places them in the Apps external Downloads
directory (retrieved with context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
).
The App offers a sharing feature, which utilizes the ACTION_SEND
intent to export a file to another application.
I have observed different behavior with my App, based on the phones Android OS version. My test is simple: Share the same file (a single photo) to 3 different Apps: 1) WhatsApp, 2) Gmail 3) InShot.
The App is using a FileProvider to export the files as it's the standard on Android now.
Support library version is 27.1.0.
The relevant code parts looks like this:
Nothing special, just the default config and a link to the paths.xml. applicationId and filesAuthority placeholders are correctly replaced.
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}${filesAuthority}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths"/>
</provider>
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path
name="Download"
path="Download"/>
</paths>
createSharingIntent(context: Context, videos: List<Video>): Intent
var hasVideos = false
var hasImages = false
var items = emptyArray<ClipData.Item>()
var contentTypes = emptyArray<String>()
// Create ClipData items & contentTypes which help to further specify the Intent contents.
for (video in videos) {
items += ClipData.Item(FileProvider.getUriForFile(context, BuildConfig.FILES_AUTHORITY, File(video.path)))
contentTypes += (if (video.isJpg) IMAGE_JPG else MediaFormat.MIMETYPE_VIDEO_AVC)
if (video.isJpg) {
hasImages = true
} else {
hasVideos = true
}
}
// Build the Intent
val intent: Intent
val intentType = when {
hasImages && hasVideos -> "*/*"
hasImages -> IMAGE_JPG
else -> MediaFormat.MIMETYPE_VIDEO_AVC
}
intent = if (items.size > 1) {
Intent().setAction(Intent.ACTION_SEND_MULTIPLE)
.setType(intentType)
.putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(items.map { it.uri }))
} else {
Intent().setAction(Intent.ACTION_SEND)
.setDataAndTypeAndNormalize(items[0].uri, intentType)
.putExtra(Intent.EXTRA_STREAM, items[0].uri)
}
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION.or(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
// Specify more detail in ClipData - which might be helpful for
val clipData = ClipData("Export", contentTypes, items[0])
for (i in 1 until items.size) {
clipData.addItem(items[i])
}
intent.clipData = clipData
return intent
Intent intent = createSharingIntent(activity, videos)
Intent chooser = Intent.createChooser(intent, activity.getString(R.string.share_files));
activity.startActivity(chooser);
What is missing to create the same behavior - and most importantly - ensure WhatsApp and other apps can see the image properly, like Gmail?
This question was resolved by ensuring the filename is passed along in lowercase.
Some Android phones did trip over this while others didn't.