android getUriForFile fails for external_files_path - android

* NEW INFO *
Singlestepping into the FileProvider code, I see that external-files-path is not actually rooted at /storage/emulated/0/ but at /storage/emulated/0/Android/data/com.perinote.perinote/files/. So, I ask the question, how do I share a file in external storage /DCIM?
I'm passing an "external" file to an Intent and getting an "IllegalArgumentException: Failed to find configured root that contains ..." error.
Not sure what's missing. Doing something similar with a "cache" file works.
Here's my provider part of my manifest:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.perinote.image_access"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
The "filepaths.xml" file:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path
path="DCIM/Perinote/" name="images" /> <!-- must be same as ImageData.getPerinoteDirectory -->
</paths>
And the snip of code, where "file" is the file name (without path) in question:
Intent intent = new Intent (Intent.ACTION_SEND);
intent.setType ("plain/text");
intent.putExtra (Intent.EXTRA_EMAIL, new String[] {address});
intent.putExtra (Intent.EXTRA_SUBJECT, subject);
intent.putExtra (Intent.EXTRA_TEXT, body);
Uri uri = FileProvider.getUriForFile (getContext (), "com.perinote.image_access", file);
intent.putExtra (Intent.EXTRA_STREAM, uri);
intent.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags (Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult (intent, ACTIVITY_REQUEST_SEND_EMAIL);
It's failing in getUriForFile().
When I use a file manager app on the device, the file is definitely there, under DCIM/Perinote/
For example, using the debugger, file is "IMG_20171130_204015.jpg".
And opening an explorer window on Windows shows the path
This PC\Moto G (4)\Internal shared storage\DCIM\Perinote
and the file appears in the list below.

You should start with changing <external-files-path to <external-path..

Related

Android: Send e-mail via intent with file as attachment

There are many posts on this topic, but I can't find the solution for my problem...
Following: I would like to send a file out of my app via an e-mail attachment.
Sending the file via Whatsapp, save to Google Drive,... works, but not for K-9 Mail or Gmail ("Unable to attache file" Toast message is displayed).
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
intentShareFile.setType("application/zip");
intentShareFile.putExtra(Intent.EXTRA_STREAM, Uri.parse("/sdcard/Download/ExportFile.zip"));
//intentShareFile.putExtra(Intent.EXTRA_TEXT, "message");
intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intentShareFile.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivity(Intent.createChooser(intentShareFile, "Share File"));
I don't understand why it works for all apps, except e-mail apps.
Can anyone help me out?Thanks in advance.
I think your issue is related to the path of the file you are using here (i.e Uri.parse("/sdcard/Download/ExportFile.zip"))
There is a code example to send an email with an attachment on the Android Developers docs here. In the example they pass the Uri as follows:
Uri.parse("content://path/to/email/attachment"). Notice this is called a ContentUri, read more about it form here.
Hope this turns useful for you!
Amr EIZawawy is right. I need to create the Uri of the file and use the File Provider API.
I don't know if I did too much, but this is my solution:
Create a file called "file_paths.xml" in a directory "xml" (create first) within your "res" directory (sibling of layout directory).
The file needs to contain the path to the file you want to share. For the external Download directory this is:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="download" path="Download/"/>
</paths>
// The internal path you'd be <files-path name.../> see [FileProvider][1] for all possibilities
Define the File provider within the AndroidManifest.xml with a meta-data link to the just created XML file:
<application
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.<your-app-package-name>.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" /> // Link to the above created file
</provider>
...
Implement the code:
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
intentShareFile.setType("application/zip");
File fileDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File newFile = new File(fileDirectory, "ExportFile.zip");
String authority = "com.bennoss.myergometertrainer.fileprovider"; // Must be the same as in AndroidManifest.
Uri contentUri = FileProvider.getUriForFile(getContext(), authority, newFile);
intentShareFile.putExtra(Intent.EXTRA_STREAM, contentUri);
//intentShareFile.putExtra(Intent.EXTRA_TEXT, "xxx");
intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intentShareFile, "Share File"));
For more information see: FileProvider

Flutter share image intent

I am trying to share an image via classic Intent. I have added the following items:
file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="my_images" path="." />
</paths>
Manifest:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
And finally MainActivity.java:
private void shareFile(String fileName) {
Intent share = new Intent(Intent.ACTION_SEND);
Uri uri = FileProvider.getUriForFile(this, "com.example.android.fileprovider", new File(this.getApplicationInfo().dataDir + "/app_flutter/userphotos", fileName));
share.setData(uri);
share.setType("image/png");
share.putExtra(Intent.EXTRA_STREAM, uri);
share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(share, "Share"));
}
The problem I am facing is that image I am trying to share has the following path:
/data/user/0/shoedrobe.innovativeproposals.com.shoedrobe/app_flutter/userphotos/receipt20181101094430.jpg
However the FileProvider is trying to access it from here:
java.lang.IllegalArgumentException: Failed to find configured root
that contains
/data/data/shoedrobe.innovativeproposals.com.shoedrobe/app_flutter/userphotos/receipt20181101094430.jpg
For saving images I am using package path_provider and I am saving items under
getApplicationDocumentsDirectory(), which in Android is AppData directory.
I am not sure why FileProvider decided suddenly to go from /data/user/0/ to /data/data/ folder, therefore any help or tips regarding this matter would be highly appreciated.
Update:
I have updated the code as per recommendations and replaced the Uri under MainActivity.java with the following line:
Uri uri = FileProvider.getUriForFile(this, "com.example.android.fileprovider", new File(this.getDir("flutter", Context.MODE_PRIVATE).getPath() + "/app_flutter/userphotos", path));
Nonetheless, the problem still persists (same exception that file is supposed to be under /data/data/<package> instead of /data/user/0. I have also tried to add additional persmissions to my AndroidManifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
but it did not work as well. Could the problem lie with my file_paths.xml file?
In the end, none of the proposed solutions did not work; neither using extenal-path, nor the other solution.
The way how I managed in the end to share file was to actually copy it to a temporary (cache) directory first (in flutter path_provider it's getTemporaryDirectory()) and update file_paths.xml to following:
<cache-path name="image" path="."/>
and finally under MainActivity.java:
Uri uri = FileProvider.getUriForFile(this, "com.example.android.fileprovider", new File(this.getCacheDir(), path));
Under the hood, Flutter's getApplicationDocumentsDirectory() is using getDir() instead of dataDir, which could be different.
From the Android documentation on getDataDir(), which is equivalent to this.getApplicationInfo().dataDir:
Returns the absolute path to the directory on the filesystem where all private files belonging to this app are stored. Apps should not use this path directly; they should instead use getFilesDir(), getCacheDir(), getDir(String, int), or other storage APIs on this class.
The returned path may change over time if the calling app is moved to an adopted storage device, so only relative paths should be persisted.
Therefore, to ensure consistency between the directory used for saving images by Flutter and the directory used to retrieve the files via FileProvider, the getUriForFile line in MainActivity.java could be modified like this:
FileProvider.getUriForFile(this, "com.example.android.fileprovider", new File(<your application context>.getDir("flutter", Context.MODE_PRIVATE).getPath() + "/app_flutter/userphotos", fileName));
... replacing <your application context> with the variable storing your application's context, if any.

How to view / show a PDF file using FileProvider? Shows blank screen only

An Android app can create PDF files which should be stored to the external storage and viewed or shared by the user.
Creating, storing and sharing the file is no problem. However viewing the file fails and only a blank screen is shown.
This is my code:
<!-- Provider definition in manifest -->
<provider
android:authorities="com.example.MyApp.fileprovider"
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/file_paths" />
</provider>
<!-- file_path.xml in xml resource dir -->
<?xml version="1.0" encoding="utf-8"?>
<path xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="ExternalDir" path="Documents/MyApp" />
</path>
// Sharing and viewing
File baseDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
File appDir = new File(baseDir, "MyApp");
appDir.mkdirs();
File pdfFile = new File(appDir, "Test.pdf");
savePDFToFile(pdfFile)
Uri fileUri = FileProvider.getUriForFile(this, "com.example.MyApp.fileprovider", pdfFile);
// View
Intent viewIntent = new Intent(Intent.ACTION_VIEW);
viewIntent .setDataAndType(fileUri, "application/pdf");
viewIntent .addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
viewIntent .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(viewIntent , "View"));
// Sharing
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.setType("application/pdf");
sharingIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
startActivity(Intent.createChooser(sharingIntent, "Share"));
Creating/Writing the PDF file is no problem. Thus there seems to be no problem with the permissions. Sharing the file (e.g. saving it to Google Drive or sending it via email) is also no problem. Thus the FileProvider config and Uri creation seems to be correct.
So, what is wrong with the viewing intent? When testing this in the Emulator using API 26 only a blank page is shown. A user running the same code on a device with API 26 gets a message, that file access is not possible (without a reason).
However the file exists and access permission shouldn't be problem because otherwise the sharing intent would not work as well, would it?
How to solve this?

Android: How to share images from package directory?

I want to share multiple image from application package directory. My files locate in following path as like. /data/data/com.example. Now i can get images path. But not attach any images to sharing application. as like whats app, Message and Facebook etc. I have implemented permition in manifest file. See below my try.
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(saveList.get(0).toString())));
startActivity(Intent.createChooser(intent, "Share"));
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
intent.putExtra(Intent.EXTRA_SUBJECT, "Here are some files.");
intent.setType("image/*"); /* This example is sharing jpeg images. */
ArrayList<Uri> files = new ArrayList<Uri>();
for(String path : saveList /* List of the files you want to send */) {
File file = new File(path);
Uri uri = Uri.fromFile(file);
files.add(uri);
}
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, files);
startActivity(intent);
Implemented permission is below.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
It is compatible with scoped storage. So don't need any storage permission.
fun shareFile(){
var intentBuilder: ShareCompat.IntentBuilder
intentBuilder = ShareCompat.IntentBuilder.from(this)
.setType("image/jpg")
intentBuilder.addStream(FileProvider.getUriForFile(this.baseContext!!
, getString(R.string.package), File(path)))
val intent = intentBuilder.intent.setAction(Intent.ACTION_SEND).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(Intent.createChooser(intent, "Send to "))
}
In Manifest
<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>
Create a xml file
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path path="/" name="allfiles" />
<external-path path="/" name="externalfiles" />
<root-path name="external_files" path="/storage/" />
</paths>
First, Uri.fromFile() will not work in your case, because third-party apps have no access to your internal storage.
Second, using a file: Uri, such as from Uri.fromFile(), appears as though it will be banned in the next version of Android.
Instead, you need to use some sort of ContentProvider, such as FileProvider, to make the files available to third-party apps. This is covered in the documentation.

Upload excel file to Dropbox or Google Drive?

I'm currently developing an application which creates an excel file(with apache poi) from the sqlite database in android. Everything is working well, except that I can't send the file to another app.
I implemented an fileprovider so I other apps can have permission to my file:
AndroidManifest.xml:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.ria.masterdetailnoten"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"></meta-data>
</provider>
file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="backup" path="backup/"/>
</paths>
ExcelFile.java:
Uri uriFile = FileProvider.getUriForFile(ct, "com.example.ria.masterdetailnoten", backupFile);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setDataAndType(uriFile, "application/vnd.ms-excel");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.createChooser(intent, "Choose");
ct.startActivity(intent);
I know for sure that my file is being created and I can read it again with Apache POI. So the only things which doesn't work is sending the intent to another app.
As an example if I choose Google Drive in the intent chooser then following error appears:
"Upload failed: request contained no data."
If I open it with the Mailbox App following error appears:
"Mailbox could not read the selected file"
I hope you can help me guys, I appreciate your help!
Have you tried using: putExtra(Intent.EXTRA_STREAM, uriFile) instead of: setData(AndType)?
I've managed to work this out with intent.selector = Intent(Intent.ACTION_SEND).apply { data = Uri.parse("file:") }

Categories

Resources