Hello-World of FileProvider - android

This question contained several sub-questions. I am forking these, starting by this question. I'll eventually clean up by deleting this question.
The following program will in theory share a hello-world text file. The code runs, but sharing to either Dropbox or to Gmail (by way of just two concrete examples) fails.
public class MainActivity extends Activity {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String filename = "hellow.txt";
String fileContents = "Hello, World!\n";
byte[] bytes = fileContents.getBytes();
FileOutputStream fos = null;
try {
fos = this.openFileOutput(filename, MODE_PRIVATE);
fos.write(bytes);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
File file = new File(filename);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
shareIntent.setType("application/txt");
startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to)));
file.delete();
}
}
Aside from adding a value for send_to in res/values/strings.xml, the only other pair of changes I did to the generic Hello, World that Eclipse creates is adding the following <provider> tag in AndroidManifest.xml:
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mycorp.helloworldtxtfileprovider.MainActivity"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/my_paths" />
</provider>
<activity
android:name="com.mycorp.helloworldtxtfileprovider.MainActivity"
...
... and adding the following in res/xml/my_paths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="files" path="." />
</paths>
My main question is the first, but while you're at this topic, a discussion of questions 2-4 would also be interesting.
Why does the program above fail?
Is it indeed the case that if one needs a custom ContentProvider, then one needs to extend that class, but if one just needs a FileProvider, then one can use that class without derivation?
In this code, I needed to use filename twice—once with openFileOutput and another with new File(). Is there a way to avoid this duplication (that would guarantee that the same file is being referenced)?
Is it safe to delete the file right after startActivity(..) is called, or is it necessary to devise a callback to wait learning that the file has been uploaded/shared. (The real file may take some time to share/upload.)
Edit
The code runs fine and shows a list of apps to send to.
If I select Dropbox, I can select the location just fine. Dropbox sends the notifications "Uploading to Dropbox" followed by "Upload failed: my_file.txt".
If I select Gmail, I can fill the recipient and the file appears to be attached, but after "sending message.." I get "Couldn't send attachment".

1.
Use FileProvider.getUriForFile(...) to construct the URI. This will direct the started activity to your FileProvider (which can then serve the file from your app's private files directory). Uri.fromFile(...) does not work because the started activity will try to directly access the private directory.
Set FLAG_GRANT_READ_URI_PERMISSION so that the started activity is granted read permission for the URI constructed by the FileProvider.
Finally, "text/plain" might work better than "application/txt" as MIME type.
I've had some problems getting this to work consistently across devices. This is my best bet so far (will edit if I ever refine it):
final String auth = "org.volley.sndcard_android.fileprovider";
final Uri uri = FileProvider.getUriForFile(activity, auth, file);
final String type = activity.getContentResolver().getType(uriForFile);
final Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setDataAndType(uri, type);
shareIntent.putExtra(Intent.EXTRA_STREAM, uriForFile);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
final Intent chooser = Intent.createChooser(shareIntent, "<title>");
activity.startActivity(chooser);
Setting only the type works on my Nexus 5 but not on my Samsung tablet. It seems the Samsung tablet needs the URI as data in order to grant the permission. Also note that intent.getData() cancels any previous calls to intent.setType() and vice versa, so you have to use the combined method, as done above.
Gmail seems to interpret the additional data as a default To-address. Highly annoying! If anyone has a better solution, please share it (pun intended, I'm from Gothenburg).

2.Yes, it is indeed. You see that ContentProvider is an abstract class, so to use custom content provider one must have to extend it. As FileProvider is a subclass of ContentProvider (which is not abstract), programmers can use FileProvider without subclassing it.
3.For ensuring same file you can follow the sample code below -
String fileName = "my_file.txt";
String fileData = "sample file content";
// Get the file
File file = new File(this.getFilesDir(), fileName);
if (!file.exists()) {
file.createNewFile();
}
// Write data to file
FileWriter fileWriter = new FileWriter(file);
fileWriter.write(fileData);
fileWriter.close();
// Get the uri
Uri uri = Uri.fromFile(file);
// Share the file with corresponding uri
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
shareIntent.setType("application/txt");
startActivity(Intent.createChooser(shareIntent, "Send To"));
4.No, it's not safe to delete the file right after you call startActivity(). Because startActivity() is non-blocking function call and it'll return immediately. You have to wait for the time the file is being shared. You can do that by using startActivityForResult() . See if it serves the purpose.

Related

Why would an Android Open File intent hang while opening an image?

My Problem: When openFile() intent attempts StartActivityForResult, the application hangs with a blank screen and circular cursor. The file(s) are stored to app.
What I have done: Before and after this issue I researched how to open files by use of Intents. I found a number of similar but different approaches and either used the literal example or a combination of various examples to see if I could find and resolve the issue (see some below). I'm not receiving any type of error message that I have found, and I had the FileUriExposedException previously but resolved it. These files are stored to the app and it may be a permissions issue and have tried what I know, including updating the manifest with for External Read and Write and added a flag setting on the intent for access.
What I'm trying to do: I'm trying to learn how to open files through intents with simple JPG images, then eventually expand to opening various file types after I understand how to do so.
Below is the current code I'm using and it included a MimeTypeMap that I tried in place of "image/jpeg" in case my syntax was not correct on the MIME Type, which did not seem to have an impact.
private void openFile(Uri uri, String fName) {
MimeTypeMap myMime = MimeTypeMap.getSingleton();
String mimeType = myMime.getMimeTypeFromExtension(getUpperBound(fName));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("content://" + uri), "image/jpeg");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent, 2);
}
An image of the resulting issue hanging and never opening below:
Some of the referenced links I tried:
https://developer.android.com/guide/components/intents-common
ACTION_VIEW intent for a file with unknown MimeType
Open File With Intent (Android)
http://www.androidsnippets.com/open-any-type-of-file-with-default-intent.html
I was able to resolve my issue combined with S-Sh's second suggestion of considering the use of FileProvider. I had to perform some refinements and ended up with the following to work. I also provided links to the sources in which I used.
Context of the solution:
As a note to future readers as to the context of this solution within my Android app, the code below is launched by clicking a "clickable" TableRow in a TableLayout. The TableLayout lists each filename and file ID as a TableRow. Once a row is clicked, the onClick method of the selected TableRow the filename is acquired and the File class and FileProvider are used and filename passed to create an Uri. This Uri is then passed to an openFile(Uri uri) method I created encapsulating (so-to-speak) the Intent used to open the file.
Code
Adding of the FileProvider to the AndroidManifest.xml' within the` tag:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.mistywillow.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
Creating within res directory the file_paths.xml and xml directory (res/xml/):
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="myFiles" path="." />
</paths>
The onClick capturing the filename and preparing the Uri to be passed to the openFile():
row.setOnClickListener(v -> {
TableRow tablerow = (TableRow) v;
TextView sample = (TextView) tablerow.getChildAt(1);
String result = sample.getText().toString();
File filePaths = new File(getFilesDir().toString());
File newFile = new File(filePaths, result);
Uri contentUri = getUriForFile(getApplicationContext(), "com.mydomain.fileprovider", newFile);
openFile(contentUri);
});
The openFile(Uri uri) method containing the Intent to open a file:
private void openFile(Uri uri){
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(intent, 2);
}
Links referenced:
https://developer.android.com/reference/androidx/core/content/FileProvider
FileProvider - IllegalArgumentException: Failed to find configured root
Try use origin Uri itself:
intent.setDataAndType(uri, "image/jpeg");

Unable to open PDF from internal storage for viewing on Android 8 and 9 using File Provider

For Android 8 and 9 only.
I have a PDF filer here -
String url = "file:///storage/emulated/0/Android/data/com.verna.poc/files/Download/mypdf.pdf";
I'm trying to open this file for viewing using this -
File file= new File(url);
file.setReadable(true, false);
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri pdfUri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", file);
intent.setDataAndType(pdfUri, "application/pdf");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
List<ResolveInfo> resInfoList = getApplicationContext().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
getApplicationContext().grantUriPermission(packageName, pdfUri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
Intent in = Intent.createChooser(intent, "Open File");
startActivity(in);
The File chooser option is opening and when I open the file using Google PDF reader, the PDF reader opens and closes immediately. Whats wrong in my code ?
access the file from within the app which owns that private directory - an Intent won't cut it, because this could have been sent by just any application. and if it has to be a file-chooser Intent, then create a shared directory for your app on the SD card, where any application can access it; the regular Downloads directory would also be suitable for that.
another option (as initially suggested) would be to create a simple file-chooser, which resides within the application, so that no Intent would be required to select a file... this all has certain advances and dis-advances; choose the one possibility, which suits you the best... in general, it's private vs. shared storage location.
You are trying to share a file from internal storage with the another app. You will need to create a file provider for this to work. You will need to specify a directory which you want the file provider to generate Uris for
FileProvider is a special subclass of ContentProvider that facilitates secure sharing of files associated with an app by creating a content:// Uri for a file instead of a file:/// Uri.
A FileProvider can only generate a content URI for files in directories that you specify beforehand. To specify a directory, specify the its storage area and path in XML, using child elements of the element. For example, the following paths element tells FileProvider that you intend to request content URIs for the images/ subdirectory of your private file area.
This answer has a good example on this
link
Here is the doc page for FileProvider
FileProvider
In this we first save the file to internal storage and then read from it using external app.
I use below method to save the file in internal storage :
private void savePDFtoInternalStorage(byte[] pdfAsBytes){
//Save in internal memo cache
File directory = mFragmentActivity.getFilesDir();
//updating path for pdf to match with file_path.xml
mCardStmtFile = new File(directory.getAbsolutePath(), "sample.pdf");
OutputStream outputStream = null;
try {
PbLogger.e(TAG, "writing to mStmtFile");
outputStream = new FileOutputStream(mCardStmtFile, false);
outputStream.write(pdfAsBytes);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
PFB the file provider declared file_path.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path path="/" name="secretpdf" />
</paths>
PFB the Android Manifest Entry for file provider :
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.***.********.provider"
android:exported="false"
android:grantUriPermissions="true">
<!-- ressource file to create -->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths">
</meta-data>
</provider>
Use below code to launch PDF :
Intent intentShareFile = new Intent(Intent.ACTION_VIEW);
if (mStmtFile.exists()) {
intentShareFile.setType("application/pdf");
Uri fileUri = FileProvider.getUriForFile(
mFragmentActivity,
"com.****.********.provider",
mCardStmtFile);
intentShareFile.putExtra(Intent.EXTRA_STREAM, fileUri);
intentShareFile.putExtra(Intent.EXTRA_SUBJECT,
getDescription());
//adding grant read permission to share internal file
intentShareFile.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intentShareFile, "Share File"));
}
I got the issue. When you are making a new file using, File file = new File(path), don't add file:// infront of the path.
This is correct -
String url = "/storage/emulated/0/Android/data/com.verna.poc/files/Download/mypdf.pdf";
File file= new File(url);
This is wrong -
String url = "file:///storage/emulated/0/Android/data/com.verna.poc/files/Download/mypdf.pdf";
File file= new File(url);

Gmail 5.0 app fails with "Permission denied for the attachment" when it receives ACTION_SEND intent

My app creates mails with attachments, and uses an intent with Intent.ACTION_SEND to launch a mail app.
It works with all the mail apps I tested with, except for the new Gmail 5.0 (it works with Gmail 4.9), where the mail opens without attachment, showing the error: "Permission denied for the attachment".
There are no useful messages from Gmail on logcat. I only tested Gmail 5.0 on Android KitKat, but on multiple devices.
I create the file for the attachment like this:
String fileName = "file-name_something_like_this";
FileOutputStream output = context.openFileOutput(
fileName, Context.MODE_WORLD_READABLE);
// Write data to output...
output.close();
File fileToSend = new File(context.getFilesDir(), fileName);
I'm aware of the security concerns with MODE_WORLD_READABLE.
I send the intent like this:
public static void compose(
Context context,
String address,
String subject,
String body,
File attachment) {
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("message/rfc822");
emailIntent.putExtra(
Intent.EXTRA_EMAIL, new String[] { address });
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, body);
emailIntent.putExtra(
Intent.EXTRA_STREAM,
Uri.fromFile(attachment));
Intent chooser = Intent.createChooser(
emailIntent,
context.getString(R.string.send_mail_chooser));
context.startActivity(chooser);
}
Is there anything I do wrong when creating the file or sending the intent? Is there a better way to start a mail app with attachment? Alternatively - has someone encountered this problem and found a workaround for it?
Thanks!
I was able to pass a screenshot .jpeg file from my app to GMail 5.0 through an Intent. The key was in this answer.
Everything I have from #natasky 's code is nearly identical but instead, I have the file's directory as
context.getExternalCacheDir();
Which "represents the external storage directory where you should save cache files" (documentation)
GMail 5.0 added some security checks to attachments it receives from an Intent. These are unrelated to unix permissions, so the fact that the file is readable doesn't matter.
When the attachment Uri is a file://, it'll only accept files from external storage, the private directory of gmail itself, or world-readable files from the private data directory of the calling app.
The problem with this security check is that it relies on gmail being able to find the caller app, which is only reliable when the caller has asked for result. In your code above, you do not ask for result and therefore gmail does not know who the caller is, and rejects your file.
Since it worked for you in 4.9 but not in 5.0, you know it's not a unix permission problem, so the reason must be the new checks.
TL;DR answer:
replace startActivity with startActivityForResult.
Or better yet, use a content provider.
Use getExternalCacheDir() with File.createTempFile.
Use the following to create a temporary file in the external cache directory:
File tempFile = File.createTempFile("fileName", ".txt", context.getExternalCacheDir());
Then copy your original file's content to tempFile,
FileWriter fw = new FileWriter(tempFile);
FileReader fr = new FileReader(Data.ERR_BAK_FILE);
int c = fr.read();
while (c != -1) {
fw.write(c);
c = fr.read();
}
fr.close();
fw.flush();
fw.close();
now put your file to intent,
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tempFile));
You should implement a FileProvider, which can create Uris for your app's internal files. Other apps are granted permission to read these Uris. Then, simply instead of calling Uri.fromFile(attachment), you instantiate your FileProvider and use:
fileProvider.getUriForFile(attachment);
Google have an answer for that issue:
Store the data in your own ContentProvider, making sure that other apps have the correct permission to access your provider. The preferred mechanism for providing access is to use per-URI permissions which are temporary and only grant access to the receiving application. An easy way to create a ContentProvider like this is to use the FileProvider helper class.
Use the system MediaStore. The MediaStore is primarily aimed at video, audio and image MIME types, however beginning with Android 3.0 (API level 11) it can also store non-media types (see MediaStore.Files for more info). Files can be inserted into the MediaStore using scanFile() after which a content:// style Uri suitable for sharing is passed to the provided onScanCompleted() callback. Note that once added to the system MediaStore the content is accessible to any app on the device.
Also you can try set permissions for your file:
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
And finally you can copy/store your files in external storage - permissions not needed there.
I tested it and I found out that it was definitely private storage access problem.
When you attach some file to Gmail (over 5.0) do not use the file from private storage such as /data/data/package/. Try to use /storage/sdcard.
You can successfully attach your file.
Not sure why GMail 5.0 doesn't like certain file paths (which I've confirmed it does have read access to), but an apparently better solution is to implement your own ContentProvider class to serve the file. It's actually somewhat simple, and I found a decent example here: http://stephendnicholas.com/archives/974
Be sure to add the tag to your app manifest, and include a "android:grantUriPermissions="true"" within that. You'll also want to implement getType() and return the appropriate MIME type for the file URI, otherwise some apps wont work with this... There's an example of that in the comment section on the link.
I was having this problem and finally found an easy way to send email with attachment. Here is the code
public void SendEmail(){
try {
//saving image
String randomNameOfPic = Calendar.DAY_OF_YEAR+DateFormat.getTimeInstance().toString();
File file = new File(ActivityRecharge.this.getCacheDir(), "slip"+ randomNameOfPic+ ".jpg");
FileOutputStream fOut = new FileOutputStream(file);
myPic.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
fOut.flush();
fOut.close();
file.setReadable(true, false);
//sending email
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"zohabali5#gmail.com"});
intent.putExtra(Intent.EXTRA_SUBJECT, "Recharge Account");
intent.putExtra(Intent.EXTRA_TEXT, "body text");
//Uri uri = Uri.parse("file://" + fileAbsolutePath);
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(Intent.createChooser(intent, "Send email..."),12);
}catch (Exception e){
Toast.makeText(ActivityRecharge.this,"Unable to open Email intent",Toast.LENGTH_LONG).show();
}
}
In this code "myPic" is bitmap which was returned by camera intent
Step 1: Add authority in your attached URI
Uri uri = FileProvider.getUriForFile(context, ""com.yourpackage", file);
Same as your manifest file provide name
android:authorities="com.yourpackage"
Step 2`; Add flag for allow to read
myIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

Share Privately Stored Image to External App - Android

I'm trying to save an image to my app's private filesystem then share that image with external apps. I've looked into it, and it seems like the best way to do it is just to save onto the filesDir because if you use the externalFiles directory, some devices may not have one. Here's my code so far (simplified for brevity):
Activity with my image
public Uri saveImageAndGetUri(Context context) {
String fileName = "test.png";
File imageDirectory = new File(getFilesDir(), "images");
File savedImage = new File(imageDirectory, fileName);
FileOutputStream stream;
try {
stream = new FileOutputStream(savedImage);
getCurrentImage().compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.flush();
stream.close();
} catch(Exception e) {}
return FileProvider.getUriForFile(context, "com.mydomain.mypackage.Dialogs.ShareOptions", savedImage);
}
ShareOptions.java
Uri contentUri = activity.saveImageAndGetUri(getContext()); //This calls the method above
intent = new Intent(Intent.ACTION_SEND);
getContext().grantUriPermission("com.twitter.android", contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); //Grant read permission to Twitter. Don't think this part is working
getContext().grantUriPermission("com.twitter.android", contentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); //Grant write permission to Twitter. Don't think this part is working
intent.setPackage("com.twitter.android");
intent.setType("image/png");
intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_STREAM, contentUri);
AndroidManifest.xml
<provider android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.mypackage.Dialogs.ShareOptions"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
xml/file_paths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
</paths>
As soon as the intent is sent, the Twitter app crashes. I've looked into the error log in Twitter and it seems like it may be an issue with permissions. I think somehow Twitter doesn't have permission to access the Uri even with the code I currently have.
Any and all help is appreciated!
The reason it doesn't work is many 3rd-party apps don't support FileProvider right now. See this question: Image share intent works for Gmail but crashes FB and twitter.

android: open a pdf from my app using the built in pdf viewer

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>

Categories

Resources