I know that there are a lot of questions at SO about this problem. I tried about 10 different approaches and i couldn't make it work right. I always get next error:
java.lang.IllegalArgumentException: Failed to find configured root that contains /file:/storage/emulated/0/Android/data/app.kwork/files/IMG-afbfdc57c016fb1ff7dd983a056edffa-V.jpg
Official tutorial also didn't help. I'll share my code below, maybe i do something wrong, and you know what exactly is wrong here..
Manifest:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="app.kwork.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
provider_paths.xml
<paths>
<files-path path="." name="files" />
</paths>
I save files into:
File futureStudioIconFile = new File(getExternalFilesDir(null) + File.separator + fileName);
Example of the file path: file:///storage/emulated/0/Android/data/app.kwork/files/IMG-afbfdc57c016fb1ff7dd983a056edffa-V.jpg
And try to share this file with next code:
public void openFile(String filePath){
File file = new File(filePath);
Log.d(TAG, "openFile: filePath: " + filePath);
Uri photoURI = FileProvider.getUriForFile(context, "app.kwork.provider", file);
MimeTypeMap myMime = MimeTypeMap.getSingleton();
Intent newIntent = new Intent(Intent.ACTION_VIEW);
String mimeType = myMime.getMimeTypeFromExtension(Utils.fileExt(filePath));
newIntent.setDataAndType(photoURI,mimeType);
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
newIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
context.startActivity(newIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, "No handler for this type of file.", Toast.LENGTH_LONG).show();
}
}
I tried different authorities and paths in xml, but i'm here now.
UPD: Testing on Android Oreo
Per the FileProvider documentation, it is <external-files-path> that corresponds with getExternalFilesDir() - the <files-path> you are using corresponds with getFilesDir().
You must change your paths.xml to use <external-files-path> or store your files in getFilesDir().
Example of the file path: file:///storage/emulated/0/Android/data/app.kwork/files/IMG-afbfdc57c016fb1ff7dd983a056edffa-V.jpg
This is not a file path. This is a Uri.
The file path is /storage/emulated/0/Android/data/app.kwork/files/IMG-afbfdc57c016fb1ff7dd983a056edffa-V.jpg.
Do not pass a string form of a Uri to the File constructor. You will wind up with a malformed File, and getUriForFile() will not be able to deal with that malformed File.
Related
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
I am trying to open a PDF file (already downloaded) in Android App. I am sending ACTION.View intent. The concerned code is as follows
File pdfFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()
+ File.separator + fileName);
Uri path = FileProvider.getUriForFile(MainActivity.this,BuildConfig.APPLICATION_ID + ".provider", pdfFile);
Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
pdfIntent.setDataAndType(path, "application/pdf");
pdfIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pdfIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
pdfIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
pdfIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Toast.makeText(this, "hello: "+pdfFile, Toast.LENGTH_SHORT).show();
t1 = findViewById(R.id.textView);
//t1.setText(path.toString() + "####"+pdfFile.getPath());
try{
startActivity(pdfIntent);
}catch(ActivityNotFoundException e){
Toast.makeText(this, "No Application available to view PDF", Toast.LENGTH_SHORT).show();
t1.setText("No applicatoin available");
} catch ( Exception e){
t1.setText("No App Found sorry "+ e.getMessage());
Toast.makeText(this, "No App found, sorry", Toast.LENGTH_SHORT).show();
}
finally {
//Toast.makeText(this, "This is finally block", Toast.LENGTH_SHORT).show();
}
Corresponding XML in the Manifest file is as follows
<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>
And the file_paths, content is as follows
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="Download" path="Download"/>
</paths>
Now it works fine to the extent that the PDF is being opened but it shows no content, and is rather blank. What I am suspecting is, the path of the PDF file may not be correct and hence it opens some dummy PDF perhaps? How do I provide correct path to application, I am particularly not sure about this line does in file_paths
**<external-path name="Download" path="Download"/>**
Can someone shed some light on it, how can I can fix it or debug it? What am I possibly doing wrong. I am new to Android development.
Here is my screen when opening PDF file which is basically blank (content is zero).
When I debug the code, the name of the path file is given as follows
content://com.example.openpdf.provider/Download/2019_10_22_03_37_25_pm5906.pdf
which I think is the problem (this pdf file is in the download folder by the way)
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.
I try to share a picture from my SD card.
Class MainActivity.java
Intent shareIntent = new Intent(Intent.ACTION_SEND);
File baseDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
String fileName = "/cats.jpg";
File file = new File(baseDir + fileName);
Uri uri = Uri.fromFile(file);
shareIntent.setType("image/*");
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(shareIntent.createChooser(shareIntent, "Send Picture"));
I add to my Manifest.xml:
uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
The image "cats" is in
MySmartphone\Card\DCIM\cats.jpg
When I start my App, I can choose an application to share my picture. But when I open one, it cannot send or open the image.I got often a toast "not supported" or "error to share picture". I think my uri or file is wrong, but I cannot find my mistake. I have no problem to send text.
My uri:
file:///data/user/0/com.example.jana.showpicture/files/cats.jpg
My file: /storage/emulated/0/cats.jpg
Probably you are building for Android API 24 and above. Since API 24 a new kind of security was added. See:
https://developer.android.com/about/versions/nougat/android-7.0-changes.html#perm
You now need to do a bit more, add this in your AndroidManifest.xml:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.jana.showpicture.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
/res/xml/provider_paths.xml:
<paths>
<external-path
name="external_files"
path="." />
</paths>
And Java:
Uri uri = FileProvider.getUriForFile(activity,"com.example.jana.showpicture.provider",
file);
shareIntent .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
I am implementing a File Provider, followed the doc carefully but unable to use the files in the app itself or share any file.
Added manifest, created xml file for dir sharing, generating content uri, granting uri permission, tried sharing files with cache, external, internal dir but nothing is working.
Searched the net but found nothing which is missing in the code.
Below is the code:
Manifest:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="package.widget.fileprovider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
filepaths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="widgetDoc" path="."/>
</paths>
GetUri:
private Uri getFileUri(Context context, String name){
File newFile = new File(context.getCacheDir() + File.separator + StorageUtil.INTERNAL_DIR, name);
Uri contentUri = FileProvider.getUriForFile(context, "package.widget.fileprovider", newFile);
return contentUri;
}
Code to access pdf file:
Intent target = new Intent(Intent.ACTION_VIEW);
Uri uri = getFileUri(getApplicationContext(), file);
target.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
target.setDataAndType(uri,"application/pdf");
Intent intent = Intent.createChooser(target, "Choose Pdf Viewer...");
startActivity(intent);
Code to access image:
imageview.setImageURI(getFileUri(getApplicationContext(), file));
Kindly help me out where I am going wrong, not even able to use these files in my own app too.
Thanks in advance.
I need to show these image files in a widget and now if am accessing the image files from the widget then it is giving IllegalArgumentException: Failed to find configured root however it is working fine with activity
Ideally, use setImageViewBitmap(), and make sure that your image is small (under 1MB in heap space, such as less than 512x512).
While the RemoteViews can accept a Uri, you have good way of associating Intent.FLAG_GRANT_READ_URI_PERMISSION with a RemoteViews for the purposes of populating an ImageView. And, you cannot export your FileProvider, as FileProvider does not allow that.
You can attempt to identify the user's chosen home screen and use grantUriPermission() to grant the home screen access to this Uri, but I would expect that solution to be fragile.