Doesnt attach file to Gmail - android

Programming in kotlin and using an implicit intent. I have created a txt-file and want to attach this automatically to the email created with the intent. This file is not attaching.
binding.shareAction.setOnClickListener {
lifecycleScope.launch {
val sendIntent = Intent(Intent.ACTION_SEND)
sendIntent.putExtra(Intent.EXTRA_STREAM, File("src/main/java/com/example/openlog/item_logs.txt"))
sendIntent.type = "*/*"
startActivity(Intent.createChooser(sendIntent, "SHARE"))
}
}

Need to convert File into Uri.
val contentUri = FileProvider.getUriForFile(
this,
"com.stackkotlin.provider", //use your app signature + ".provider"
newFile
)
Try below code:
val newFile = getFileFromAssets(this, "demo.txt")
val contentUri = FileProvider.getUriForFile(
this,
"com.stackkotlin.provider", //use your app signature + ".provider"
newFile
)
Log.e("filePath-----",""+contentUri)
val sendIntent = Intent(Intent.ACTION_SEND)
sendIntent.type = "*/*"
sendIntent.putExtra(Intent.EXTRA_STREAM, contentUri)
startActivity(Intent.createChooser(sendIntent, "SHARE"))
I am using asset file, Get file from assets folder.
#Throws(IOException::class)
fun getFileFromAssets(context: Context, fileName: String): File = File(context.cacheDir, fileName)
.also {
if (!it.exists()) {
it.outputStream().use { cache ->
context.assets.open(fileName).use { inputStream ->
inputStream.copyTo(cache)
}
}
}
}
For getting Uri from File we need file provider
Add FileProvider in AndroidManifest.xml
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
Add file_paths.xml file under res-> xml folder
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>

Related

Android Kotlin Can't get file from external storage [duplicate]

This question already has answers here:
Create a file from a photo URI on Android
(1 answer)
Android - Get real path of a .txt file selected from the file explorer
(1 answer)
Closed 1 year ago.
I'm trying to get an backup file from downloads folder by an intent. The intent picker work's fine
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "application/zip"
intent.flags = FLAG_GRANT_READ_URI_PERMISSION
registerForActivityResult.launch(intent)
in the file result i'm trying like this:
try {
val path: String = data.dataString.toString()
val uri = FileProvider.getUriForFile(this, applicationContext.packageName.toString() + ".provider", File(path))
val file = File(uri.path.toString())
if (file.exists()) {
restoreDatabase()
}
else{
//error
}
} catch (e: Exception) {
Log.e("pickFile", e.toString())
}
int the exception i'm getting the error (the error ocurrs in line: val uri = FileProvider.getUriForFile..... ):
"java.lang.IllegalArgumentException: Failed to find configured root that contains /content:/com.android.externalstorage.documents/document/primary%3ADownload%2Fdatabase.zip"
i have an provider configured:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="myPackage.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
providers xml
<paths>
<external-files-path
name="external_files"
path="." />
<external-path
name="external"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>

IllegalArgumentException: Failed to find configured root with file provider

I am using :
/* to pass to dl manager https://github.com/tonyofrancis/Fetch */
val destinationPath = contexct!!.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
as destination path to store file
println("$TAG , downloadAndShow >> destinationPath : $destinationPath , ${destinationPath .exists()}")
it prints: destinationPath
: storage/emulated/0/Android/data/ir.vasl.datyar.lawyer/files/Download/tiny-824-۲۰۲۱-۱۰-۳۱-۱۴-۱۳-۳۲.jpg , true
In AndroidManifest
<application
android:requestLegacyExternalStorage="true">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:enabled="true"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
And file_paths:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files"
path="." />
<files-path
name="files-path"
path="." />
<external-files-path
name="Download"
path="." />
<cache-path
name="cache_files"
path="." />
</paths>
but on onComplete
method of dl manager listener
val uriFromFile = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
/* Error Line */
FileProvider.getUriForFile(
it,
it.applicationContext.packageName + ".provider",
File(download.file)
)
} else Uri.fromFile(File(download.file))
Faced error :
IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.pachagename.project/files/Download/tiny-824-۲۰۲۱-۱۰-۳۱-۱۴-۱۳-۳۲.jpg
File(download.file).exists() returns true
Solution:
val destinationPath = File(it.filesDir, "files")
<external-files-path
name="files"
path="files/" />

Failed to find configured root that contains /data/data/com.company.app/files

Not sure why I'm getting this as I thought I followed the example here correctly.
Many others have asked questions around this same thing but after looking through the suggestions it's still broken.
The full exception looks like this:
java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.company.app/files/app_videos/VIDEO_20210202_184514.mp4
at androidx.core.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:744)
at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:418)
The manifest looks like this:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
The provider_paths.xml file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="/"/>
<external-cache-path name="external_cache" path="." />
<external-path name="external" path="." />
<external-files-path name="external_files" path="." />
<files-path name="app_videos" path="files/" />
</paths>
The code I am using (pretty much cut and paste from Google's site) looks like this:
val imagePath = File(context.filesDir, "app_videos")
Timber.i("imagePath: ${imagePath.name}")
val newFile = File(imagePath, name)
Timber.i("newFile exists: ${newFile.exists()} length: ${newFile.length()}")
val uri = FileProvider.getUriForFile(context.applicationContext, context.packageName + ".provider", newFile)
Timber.i("Adding Uri: $uri")
That last line never executes as the crash happens on the line before it.
Now, I know that I saved the file off here:
/data/user/0/com.company.app/files/VIDEO_20210202_/data/user/0/com.standardandroid.stalkersport/files/VIDEO_20210202_200455.mp4
I know it's there because I can play it back just fine using other code
So, I know the file exists. I just can't create a Uri for it.
Can anyone see what I am doing wrong please?
OK...got it. Needed to tweak a couple things. First the code should look like this
private fun shareMultipleVideos(names: List<String>, context: Context) {
val uris: ArrayList<Uri> = ArrayList()
for (name in names) {
val videoFile = File(context.filesDir, name)
Timber.i("videoFile ${videoFile.name} exists: ${videoFile.exists()}")
val uri = FileProvider.getUriForFile(context.applicationContext, context.packageName + ".provider", videoFile)
Timber.i("Adding Uri: $uri")
uris.add(uri)
}
val intent = Intent()
intent.action = Intent.ACTION_SEND_MULTIPLE
intent.putExtra(Intent.EXTRA_SUBJECT, "Shared files")
intent.type = "video/mp4"
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
Timber.i("Intent: $intent")
try{
ContextCompat.startActivity(context, Intent.createChooser(intent, "Shared Videos"), null)
} catch (e: Exception) {
Timber.e("Exception starting activity. \nException was ${e.message}\n Stack trace to follow:\n ${e.stackTrace}")
}
}
Next, the paths xml file needed to be tweaked a bit. Here are the results there:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="/"/>
<external-cache-path name="external_cache" path="." />
<external-path name="external" path="." />
<external-files-path name="external_files" path="." />
<files-path name="app_videos" path="." />
</paths>

Android Intent to Gmail not attaching file by FileProvider "Unable to attach file"

When attaching file to Gmail I briefly see the file in attachments and then get Toast saying "Unable to attach file" and then it's gone. It works fine with Drive, Discord and other apps..
Also the file stays in attachments on emulator but when I send it, the mail is send without attachments. I have a simple .csv file and attach it via FileProvider.
Tried writing to internal storage, didn't help.
val fileLocation = File(requireContext().getExternalFilesDir("data"), "data.csv")
// Saving the file into device
val streamOut =
FileOutputStream(fileLocation)
streamOut.write(myString.toByteArray())
streamOut.close()
// Exporting
val contentUri = FileProvider.getUriForFile(
requireContext(),
"mypackage.fileprovider",
fileLocation
)
val fileIntent = Intent(Intent.ACTION_SEND)
.setType("text/csv")
.putExtra(Intent.EXTRA_SUBJECT, "Data")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, contentUri)
val chooser = Intent.createChooser(
fileIntent,
requireContext().resources.getText(R.string.send_to)
)
chooser.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val resInfoList: List<ResolveInfo> = requireActivity().packageManager
.queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY)
for (resolveInfo in resInfoList) {
val packageName = resolveInfo.activityInfo.packageName
requireActivity().grantUriPermission(
packageName,
contentUri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
requireActivity().startActivity(
chooser
)
provider_paths
<paths>
<external-files-path
name="data"
path="." />
</paths>
Manifest
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="mypackage.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
Solved it by changing file_paths.xml according to this template:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>
Can you try to update your file_paths.xml to use a specific path with the external-path and try if it works?
See my working solution below. It uses multiple attachments but it works with the Gmail app:
java class
private void prepareEmail(File report, List<Expense> openExpenses) {
Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.setType("message/rfc822");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{preferences.getEmailReceiver()});
intent.putExtra(Intent.EXTRA_SUBJECT, preferences.getEmailSubject());
intent.putExtra(Intent.EXTRA_TEXT, preferences.getEmailBody());
ArrayList<Uri> uris = new ArrayList<>();
uris.add(FileProvider.getUriForFile(getApplicationContext(), "my.package", report));
for (Expense expense : openExpenses) {
if (expense.getType() == ExpenseType.EXPENSE.getValue()) {
File file = new File(expense.getReceipt());
uris.add(FileProvider.getUriForFile(getApplicationContext(), "my.package", file));
}
}
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
startActivity(Intent.createChooser(intent, getResources().getString(R.string.report_report_send)));
}
file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="Android/data/my.package/files/Pictures" />
<external-path name="my_pdfs" path="Android/data/my.package/files/Documents" />
<external-path name="my_reports" path="Android/data/my.package/files" />
<files-path name="files" path="." />
</paths>
manifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="my.package"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>

How to open file from storage by Provider in Android 8.1

I want to open and install a apk file from storage, i used code below:
File fileToOpen = new File(path);
MimeTypeMap mime = MimeTypeMap.getSingleton();
String ext = path.substring(path.lastIndexOf(".") + 1);
String type = mime.getMimeTypeFromExtension(ext);
if (fileToOpen.exists()) {
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", fileToOpen);
intent.setDataAndType(uri, type);
} else {
intent.setDataAndType(Uri.fromFile(fileToOpen), type);
}
context.startActivity(intent);
}
and in my manifest file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<provider
android:authorities="${applicationId}.provider"
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_path" />
</provider>
my provider_path.xml:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path path="." name="external_files" />
</paths>
I have to request permission in runtime and accepted, but after i call this method, nothing happen. File is already existed, what happend in my code? please help me find it, thank so much :((
To request APK for installation,
File file = new File("YOUR_FILE_PATH");
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file);
} else {
uri = Uri.fromFile(file);
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent);
Your manifeast should be,
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="YOUR_PACKAGENAME_HERE.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
And provider_path should be (It will cover almost all of available devices),
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files"
path="." />
<root-path
name="external_files"
path="/storage/" />
</paths>

Categories

Resources