I would like to open my documents saved in application storage using third party applications like polaris office,quickoffice,kingsoft docs etc using chooser.I would like to grant permission to edit the document as well.
If i open the document using FLAG_GRANT_READ_URI_PERMISSION,the document opens fine but if i use FLAG_GRANT_WRITE_URI_PERMISSION insted the external application pushedup but donot display the document selected.Incase of polaris office,the document opens fine using FLAG_GRANT_READ_URI_PERMISSION but if i use FLAG_GRANT_WRITE_URI_PERMISSION it says document type not supported.
The code i use is
File file = new File(
getFilePath(data.getUniqueId(), data.getFileName()));
Uri uri = FileProvider.getUriForFile(context, "com.example.com",
file);
Intent intent = new Intent();
intent.setAction(Intent.ACTION_EDIT);
intent.setDataAndType(uri, "application/pdf");
intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Intent chooserIntent=Intent.createChooser(intent, uri.toString());
context.startActivity(chooserIntent);
In manifest i have mentioned
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.com"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/my_paths" />
</provider>
Please let me know where i am wrong.
I would like to provide temporary permission to third part apps to open and edit the document selected.The documents need to be stored in application storage space.
instead of the line:
intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
just put the line:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
I think your provider should also have android:exported attribute set to true (in order to be used by other applications).
Related
The app needs to share a PDF file stored in the root of the cacheDir with other apps. The issue is seen on Android 12, possibly other versions too.
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/file_paths" />
</provider>
Provider paths:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="." />
</paths>
Intent:
val pdfFile = File(requireContext().cacheDir, pdfFileName)
val fileUri: Uri = FileProvider.getUriForFile(
requireContext().applicationContext,
requireContext().packageName.toString() + ".provider",
pdfFile
)
val intent = Intent()
intent.action = Intent.ACTION_SEND
intent.putExtra(Intent.EXTRA_STREAM, fileUri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.type = "application/pdf"
startActivity(Intent.createChooser(intent, "Share Document"))
The share sheet successfully opens but this exception always shows at that point and subsequently sharing to another app fails.
Writing exception to parcel
java.lang.SecurityException: Permission Denial: reading
androidx.core.content.FileProvider uri
content://uk.co.packagename.provider/cache/8BEDF7212-0DE46-42B0-9FA9-32C434BDD2F3HO.pdf
from pid=15363, uid=1000 requires the provider be exported, or grantUriPermission()
The provider as a whole cannot be exported and the URI permission appears to already be granted. I've read through the Android file sharing docs and many S/O answers but I cannot see what needs correcting, can you?
One of the limitations of FileProvider.getUriForFile() is that it does not check to see if the file exists. There are legit reasons for getting a Uri to a file that does not exist, such as for ACTION_IMAGE_CAPTURE. Still, it means that just getting the Uri is no guarantee that that the Uri is useful for reading content.
Compounding that problem is that "does the file exist" via exists() feels like it may be a bit dicey, especially for external storage.
So, it's pretty important to make sure that you have the right File object, and that it should point to an already-existing file, before you call getUriForFile().
After checking what package caused this exception it turned out to be android system. So after granting uri permission, I see no more exceptions like this in the logs:
requireContext().grantUriPermission("android", fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
The reason a security exception ("requires the provider be exported, or grantUriPermission()") is thrown is because the android system is trying to access the shared resource to give the user a preview of what would be shared.
The best approach is to use the intent.setClipData to share the resource in addition to intent.putExtra(Intent.EXTRA_STREAM, fileUri)
The code should look like this:
intent.action = Intent.ACTION_SEND
intent.setClipData(ClipData.newRawUri("", fileUri))
intent.putExtra(Intent.EXTRA_STREAM, fileUri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
#skvalex's answer above works, but it is a hacky way of giving the Android system access to the file for preview in the application share picker screen.
For a additional reference on this, please take a look at the FileProvider class as well.
My App is creating a PDF and passes it to other Apps to be displayed elsewhere. I create the file in internal storage to have to ask the user for less permissions.
I create my intent via:
Intent viewIntent = new Intent(Intent.ActionView);
Java.IO.File document = new Java.IO.File(filePath);
Android.Net.Uri contentUri = FileProvider.GetUriForFile(
_context,
_context.PackageName + ".provider",
document);
viewIntent.SetDataAndType(contentUri, GetMimeType(document));
viewIntent.SetFlags(ActivityFlags.NewTask);
viewIntent.AddFlags(ActivityFlags.GrantReadUriPermission);
viewIntent.AddFlags(ActivityFlags.ClearTask);
viewIntent.AddFlags(ActivityFlags.GrantPersistableUriPermission);
viewIntent.AddFlags(ActivityFlags.GrantPrefixUriPermission);
viewIntent.AddFlags(ActivityFlags.GrantWriteUriPermission);
Intent chooser = Intent.CreateChooser(viewIntent, "");
chooser.SetFlags(ActivityFlags.NewTask);
chooser.AddFlags(ActivityFlags.GrantReadUriPermission);
chooser.AddFlags(ActivityFlags.ClearTask);
chooser.AddFlags(ActivityFlags.GrantPersistableUriPermission);
chooser.AddFlags(ActivityFlags.GrantPrefixUriPermission);
chooser.AddFlags(ActivityFlags.GrantWriteUriPermission);
_context.StartActivity(viewIntent);
On the Google Pixel 3 XL where I test, I can open a PDF without any issues.
When I do the same on a Huawei tablet with API level 24, sometimes everything works but at other times Adobe Acrobat shows an error: This file could not be accessed. Check the location or the network and try again.
The behavior isn't deterministic, sometimes I get the error but other times everything works fine.
In the Application node of your Android Manifest make sure you've added a FileProvider definition:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.FileProvider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="#xml/provider_paths" />
</provider>
Add a Resources/xml/provider_paths file with the contents:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
You can restrict this later if needed. However, for my use case I put files in external storage, which I share from. You need to adjust this accordingly where you are sharing from.
I.e. if you are sharing the file from internal app storage you will need to add a files-path definition there too.
Then when sharing a file you simply do:
var packageName = context.ApplicationInfo.PackageName;
var fileProviderName = $"{packageName}.FileProvider";
var intent = new Intent(Intent.ActionSend);
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.SetType("image/*"); // change mime type if wanting to open in other app
intent.PutExtra(Intent.ExtraStream,
FileProvider.GetUriForFile(context, fileProviderName, new Java.IO.File(filePath)));
StartActivity(intent);
That should be enough, works fine every time for me for sharing images to another app. I don't think you need the flags for your chooser Intent, only for the inner viewIntent. Also the GrantReadUriPermission should be the only thing needed if you are providing flags.
In my app I have internal files that I would like to be able to open in external viewers depending on file type. I have a FileProvider set up like the following in the application manifest:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.myapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/paths" />
</provider>
I user the following code to show an application selector in order to view the file.
Uri contentUri = getUriForFile(getApplicationContext(), "com.myapp.fileprovider", thisFile);
Intent viewIntent = new Intent(Intent.ACTION_VIEW);
String mimeType = getApplicationContext().getContentResolver().getType(contentUri);
viewIntent.setDataAndType(contentUri, mimeType);
viewIntent.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
startActivity(viewIntent);
} catch(ActivityNotFoundException e) {}
This works well for most apps, however if I chose Google+ Photos app in the list, while the image will load fine in the Google+ Photos, it will crash immediately if I try too zoom in to the image by pinching with my fingers. It seem like Google+ Photos lost access to the file and when it tries to read it again it gets denied access.
All other apps I tried works perfectly
Any suggestions appreciated
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.
This was my original question:
I want to be able to open a pdf file
in my app using the android's built in
pdf viewer app, but i dont know how to
start other apps. I'm sure i have to
call start activity, i just dont know
how to identify the app im opening and
how to pass the file to that specific
app.
Anyone have a clue?
I just learned that the pdf viewer i have on my phone is actually made by HTC and that Adobe just barely released their android pdf viewer (which is great). So the new question is this: how do i verify that the user has installed adobe's viewer, and then how do i open the file in that app from my app?
You can programmatically determine whether a suitable application exists on the user's device, without catching exceptions.
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("path-to-document"));
intent.setType("application/pdf");
PackageManager pm = getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
if (activities.size() > 0) {
startActivity(intent);
} else {
// Do something else here. Maybe pop up a Dialog or Toast
}
AFAIK, Adobe has not documented any public Intents it wants developers to use.
You can try an ACTION_VIEW Intent with a Uri pointing to the file (either on the SD card or MODE_WORLD_READABLE in your app-local file store) and a MIME type of "application/pdf".
FileFinalpath = SdCardpath + "/" + Filepath + Filename;
File file = new File(FileFinalpath);
if (file.exists()) {
Uri filepath = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(filepath, "application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
startActivity(intent);
} catch (Exception e) {
alert.showAlertDialog(PDF_Activity.this, "File Not Started...","File Not Started From SdCard ", false);
Log.e("error", "" + e);
}
} else {
alert.showAlertDialog(PDF_Activity.this, "File Not Found...","File Not Found From SdCard ", false);
}
Although this is a pretty old topic, here is a solution for opening a PDF that is in the asset/ folder with an external PDF reader app. It uses a custom content provider: https://github.com/commonsguy/cwac-provider
Using this you can define any file to be provided from the assets/ or res/raw/ folder.
Try it! Best and easiest solution I found so far.
I was also faced same issue when was trying to display PDF on android device and finally end up with the solution (3rd party PDF library integration)
https://github.com/JoanZapata/android-pdfview
while I have tested multiple libraries for this listed below which are also working,
https://github.com/jblough/Android-Pdf-Viewer-Library
& mupdf which comes with the ndk flavour (https://code.google.com/p/mupdf/downloads/detail?name=mupdf-1.2-source.zip&can=2&q=) and need to extract with NDK and then use it in application as a jar or java etc. nice article to explain the use of this library # http://dixitpatel.com/integrating-pdf-in-android-application/
Android has a built in framework from Android 5.0 / Lollipop, it's called PDFRenderer. If you can make the assumption that your users have Android 5.0, it's probably the best solution.
There's an official example on Google's developer site:
http://developer.android.com/samples/PdfRendererBasic/index.html
It doesn't support annotation or other more advanced features; for those your really back to either using an Intent to open a full app, or embedding an SDK like mupdf.
(Disclaimer: I very occasionally do work on mupdf.)
In addition to the ones marked as answer you would need these permissions in the manifest.xml
**
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
**
Add FLAG_GRANT_READ_URI_PERMISSION
Intent intent = new Intent(Intent.ACTION_VIEW)
Uri outputFileUri = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".provider", file);
intent.setDataAndType(outputFileUri, "application/pdf");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Intent in = Intent.createChooser(intent, "Open File");
startActivity(in);
also add provider_paths.xml at res -> xml folder
and need to add below code at manifests
<application>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
tools:replace="android:resource"
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
</application>