I've had some trouble creating a function that writes a large chunk of raw data to a text file, and then shares that text file to be sent by email/whatsapp. Since Android 10 it's no longer possible to use Environment.getExternalStorageDir()
due to privacy reasons. It now seems that my file is not visible to other apps, and thus can not be opened by them. My code is as follows:
Fragment responsible for exporting:
CoroutineScope(Dispatchers.IO).launch {
withContext(Dispatchers.IO) {
val data: String = retrieveData() ?: return@withContext
val file = File(requireContext().getExternalFilesDir(null), "export.xml")
val outputStream =
requireContext().openFileOutput("export.xml", Context.MODE_PRIVATE)
outputStream.write(data.toByteArray())
outputStream.close()
val uri = FileProvider.getUriForFile(requireContext(),BuildConfig.APPLICATION_ID, file)
val shareIntent = Intent().apply {
type = "text/plain"
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM, uri)
putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_file_subject))
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(
Intent.createChooser(
shareIntent,
getString(R.string.share_file_subject)
)
)
}
}
In the manifest:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
filepaths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path
name="files"
path="/" />
</paths>
When using the Android Studio file explorer I can see that the file is created with the correct content. The Uri points to: content://myApplicationId/files/export.xml
and the file is located on my device at data/data/myApplicationId/files
but I can't seem to figure out how to make it accessible for other apps to open.
When opening it with for example the gmail app, it creates a new concept with the subject filled in but I'm getting a Toast from the gmail app saying 'Unable to add attachment'. The intent seems to fire properly so there are no exceptions occurring in my code. My guess is that the file is still not accessible for other apps for some reason.
Any suggestions?
Thanks in advance.
The Uri points to: content://myApplicationId/files/export.xml
But you are not using that Uri
. You are using Uri.fromFile()
:
val shareIntent = Intent().apply {
type = "text/plain"
putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file))
putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_file_subject))
You should be crashing with a FileUriExposedException
on Android 7.0+, so you already applied a hack to get around that. It's just that now, on Android 10+, that hack has limited value.
So, replace Uri.fromFile(file)
with uri
. Also, include addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
as part of your apply {}
lambda. And find where in your code you are configuring StrictMode
and have it complain about FileUriExposedException
, at least on debug
builds.
In addition, your I/O is messed up.
val file = File(requireContext().getExternalFilesDir(null), "export.xml")
val outputStream =
requireContext().openFileOutput("export.xml", Context.MODE_PRIVATE)
Your code assumes that openFileOutput()
puts its content in the location identified by getExternalFilesDir()
, which is incorrect. Switch the latter line to:
val outputStream = FileOutputStream(file)