In the application I receive a PDF from an API call so it is inside a byte array and I would like to display it inside the application without having to save it in the user's phone. I have tried a WebView but it has not worked. Seems like a WebView will display a PDF from an url but will not render it if you give it the PDF as a string.
I was wondering if there was a way to display a PDF inside an android application without having to save it in the user's phone?
I am doing this by writing the received bytes of the pdf file to a temporary cache DIR and then open it with an intent. So for example in an async task I download the file in the doInBackground method and do this in the onPostExecute.
#Override
protected void onPostExecute(byte[] result) {
final File reportFile = new File(context.getExternalCacheDir(), "pdf-file.pdf");
final BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(reportFile));
output.write(result);
output.close();
Uri path = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", reportFile);
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setDataAndType(path, "application/pdf");
startActivity(intent);
}
This is working quite well. The only thing you have to do is to check if the user has a PDF reader installed that is working.
The advantage I see with this solution is that you provide the user the opportunity to further do with the pdf what he or she wants (e.g. print, store, share, ...). If you just display it within a frame in your app you would quite limit the interaction possibilities (or would have to implement them all by your self).
Related
I use the following code to open a pdf form. If I use Acrobat Reader it's not possible to write back to the original file. It always makes a copy which I only could guess but newer know for shure in my app.
I know that it has to be possible to edit the original because if I open a pdf from any file manager (e.g. the one from Asus) Adobe Reader edits it directly.
Xodo PDF allows direct edit. It even supports the correct ACTION_EDIT intent but I'm afraid that our users insist to use Adobe...
How can I edit the original with Adobe?
final File file = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS) + File.separator + "x.pdf");
if(file.exists())
{
file.setWritable(true,false);
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri contentUri = FileProvider.getUriForFile(Objects.requireNonNull(getContext()),
BuildConfig.APPLICATION_ID + ".fileprovider", file);
intent.setDataAndType(contentUri,"application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY|Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent = Intent.createChooser(intent, "Open File");
startActivity(intent);
}
At the moment even Xodo doesn't work anymore but I was lucky to realized that OneDrive-PDF-Viewer does.
I am trying to send base64 image through the Intent. I've read that if the image is big, I need to put it to the internal storage first and then retrieve it from its path to send it by Intent. The problem is that android is giving me toast like this:
The upload was unsuccessful because it did not contain data.
I need to say that everything is done in my custom WebView where I passed the context. Maybe that's the issue? My API is 21. I put the code here:
this.webView.setDownloadListener((new DownloadListener() {
#Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
try {
if(url != null) {
FileOutputStream fos;
fos = context.openFileOutput("image.png", Context.MODE_PRIVATE);
byte[] decodedStr = Base64.decode(url, Base64.DEFAULT);
fos.write(decodedStr);
Intent sendIntent = new Intent();
sendIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File("file://" + context.getFilesDir() + "/chart.png")));
sendIntent.setType("image/png");
getContext().startActivity(sendIntent);
fos.flush();
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}));
My base64 looks like this:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAArgAAAIACAYAAABpWR83AAAgAElEQVR4Xux9B3xU15X+ESBUEEIVgbpAgCq9V9tg3B1jO3FLcUnsuCa2s7v/TeJksym7yaa6pttOcY2Nu8
It is much longer. I just put here a cut version to make my question more readable.
Thanks for your time!
#EDIT
I have the feeling that it is not clear what he wants to achieve. I'm making use of this tutorial: https://developer.android.com/training/sharing/send.html
But the thing which is different is base64 image
**#EDIT 2 **
Changed method to putExtra() but now other apps can't read the image
You have to keep in mind that there is a transaction limit to the 1 mb when you sending data by intent. Send to the activity only uri, and onCreate method, new started activity load image.
There are many bugs in this code:
You are writing the image to getFilesDir() via openFileOutput(), but third-party apps have no rights to files in that directory
You are using a file: Uri, which will not work on Android 7.0+
You are putting the Uri in the Intent via setData(), which is not how ACTION_SEND works (use EXTRA_STREAM)
You are not closing the file before calling startActivity() (while this should work in practice, it's the sort of thing that you will get yelled at in code reviews)
- You claim the MIME type is image/png, when the content of your file is not a PNG file
Even if you fix those, I question your premise. There are ~7 billion people on this planet. IMHO, few if any of them will want to put a base64-encoded file into some other app, as those apps will not be able to use the file. If you want to allow the user to share an image, share the image, not a base64-encoded version of the image.
I am trying to open up PDF using intent that takes the following:
Here is the code that I have:
Intent intent = new Intent(Intent.ActionVIEW)
intent.setDataAndType(Uri.parse("CONTENTURI + FILENAME","application/pdf"
try { startActivity(intent)} catch excpetion and so on.
It pops up with whatever applications I have installed, from Adobe Reader, Google PDF reader, POLARIS(as I am using Galaxy Tab 3 for testing), and none of those work. Each say unsupported document.
Does download and show the right solution in this case, or any ideas? Thanks in advance!
Following code triggers the VIEW action for pdf files (open the default PDF viewer, choose between applications capable to view PDFs or get an error if you don't have any installed).
File file = new File("your pdf path here");
if (file.exists()) {
Uri pdfPath = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(pdfPath, "application/pdf");
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
//if user doesn't have pdf reader instructing to download a pdf reader
}
}
NOTE: The PDF should not be saved local to application, or else the third party app won't have access. You should use media location.
I want to download a PDF from a url and also want to trigger catch phrase if no PDF Viewer is detected.
Here's my code:
try {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(materialPdfUrl)));
} catch (ActivityNotFoundException e) {
openDialog(getString(R.string.error),
getString(R.string.no_pdf_reader));
}
Now the problem is that ActivityNotFoundException is never triggered because it always download the PDF even if there is no PDF Viewer around. How do you suggest I do this?
EDIT:
Here's my old code:
Intent pdfIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(materialPdfUrl));
pdfIntent.setType("application/pdf");
pdfIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(pdfIntent);
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(materialPdfUrl)));
is starting an Implicit Intent and therefore will not throw ActivityNotFoundException.
If you read this http://developer.android.com/guide/components/intents-filters.html#ccases
Consider, for example, what the browser application does when the user follows a link on a web page. It first tries to display the data (as it could if the link was to an HTML page). If it can't display the data, it puts together an implicit intent with the scheme and data type and tries to start an activity that can do the job. If there are no takers, it asks the download manager to download the data. That puts it under the control of a content provider, so a potentially larger pool of activities (those with filters that just name a data type) can respond.
Therefore if no PDF viewers are found the Android Download Manager will attempt to download the file (rather than throw that exception).
If you want to view the pdf or be told you cannot view it (rather than download) then you will need to query the system manually using the PackageManager to find out if an application will respond to your intent rather than just firing and forgetting.
FYI ActivityNotFoundException will be thrown for Explicit Intent's something like:
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.facebook","NewsFeedActivity.java"));
startActivity(intent);```
I would recommend using the PackageManager to detect if the system will handle a PDF intent for you.
PackageManager packageManager = context.getPackageManager();
Intent intent = new Intent(Intent.ACTION_VIEW)
.setType("application/pdf");
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY;
if (list.size() > 0) {
// Happy days a PDF reader exists
startActivity(intent);
} else {
// No PDF reader, ask the user to download one first
// or just open it in their browser like this
intent = new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse("http://docs.google.com/gview?embedded=true&url=" + pdfUrl);
startActivity(intent);
}
See this blog post for more info on checking intents. The added benefit of this approach is that you can grey out/remove menu options before the app even tries to execute the Intent. Then you can explain to the user in a slightly more friendly way that they need to grab a PDF viewer app, or indeed apply some logic to fallback to a web based PDF viewer.
Having trialled this approach with Foxit Reader and Adobe Reader they both seem to have different behaviours. Foxit will not download the PDF for you, it will redirect you to the browser and download the file. Adobe will download the file for you then display it.
So to get round this difference once you have detected that a PDF viewer is available then you will probably want to download the PDF to the SD card, for example the downloads folder. This is probably best achieved in an AsyncTask, or you might be able to use the DownloadManager. Then open the local file Uri in the preferred reader. This should get round the difference in behaviour. Maybe open a ticket with Foxit to bring it into line with Adobe? ;)
The function startActivity() you use is not on the condition that the PDF reader is not exist, it only download the PDF from the URL, and if there are PDF Readers then it will offer a selector, just as the function of clicking on a PDF file.
you may try this code. This may helpful to you.
Intent intent = new Intent();
intent.setClassName("com.adobe.reader", "com.adobe.reader.AdobeReader");
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Uri uri = Uri.fromFile(file);
intent.setDataAndType(uri, "application/pdf");
try {
startActivity(intent);
} catch (Exception e) {
AlertDialog.Builder builder = new AlertDialog.Builder(
getApplicationContext());
builder.setTitle("No Application Found");
builder.setMessage("Download application from Android Market?");
builder.setPositiveButton(
"Yes, Please",
new DialogInterface.OnClickListener() {
#Override
public void onClick(
DialogInterface dialog,
int which) {
Intent marketIntent = new Intent(
Intent.ACTION_VIEW);
marketIntent.setData(Uri
.parse("market://details?id=com.adobe.reader"));
mProgressDialog.dismiss();
startActivity(marketIntent);
}
});
builder.setNegativeButton("No, Thanks",
null);
builder.create().show();
}
That is because your device or emulator does not have an application capable of viewing a local PDF file.
Whenever you start an intent, you should have the native app installed on the emulator to handle that intent. Ex. If you invoke an intent with Maps as the action, you would have to use the Google API's based emulator. By default, android emulator does not have a PDF reader. You could test this on a device with a PDF reader and it should work fine.
use startActivityForResult(..).
See the link here.
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/pdf");
startActivityForResult(intent, REQUEST_CODE);
You will get the result in onActivityResult(..) of your activity.
I am experimenting with Android development. I am making an app that will allow the user to browse files in a web service and view them. These files could be anything: text, pdf, pictures, etc.
Previously, I would download the file to external storage and then call Intent.SetDataAndType() and pass it the URL to the file. That would cause the Android device to bring up an app picker and let the user choose the appropriate method to look at the file.
But since I do not want the user to edit the file, only to look at it, it seemed silly to download a file to storage; a file that I didn't want to hang around. Since the file can be obtained by a URL, why don't I pass that as a parameter to the Intent.SetDataAndType()?
I tried that. The first problem was that the file name was assumed to be the name of the web service call, and that seemed to be more important than the mime-type. I changed the web service to be the same name as whatever file was attempting to be downloaded. That solved that issue.
So now, the file is being opened. But it is always being opened in a web browser. I get to choose the web browser, but I would rather have another app open it.
My code looks like this:
Intent i = new Intent(Intent.ActionView);
i.SetDataAndType(Android.Net.Uri.Parse(GetUrlToFile(fileref, fileName)), mimeType);
i.SetFlags(ActivityFlags.GrantReadUriPermission);
i.SetFlags(ActivityFlags.NewTask);
i.SetFlags(ActivityFlags.ClearWhenTaskReset); // so if the app is relaunched, we don't show the display application.
StartActivity(i);
The code is in C# because I'm using Xamarin, but I don't believe that should make a difference.
I tried using StartActivity(Intent.CreateChooser(i, "Open me")); but that didn't give me any more options for choosing.
Does anyone have any ideas as to how to do this?
I have not found a way to do this yet, so I have gone through a workaround.
Instead of using a URL, I changed my app to be a Content Provider as well. Now, when I want the file opened, I create a URI that refers to the file within my app and pass that off to an Intent. When my app is contacted by this Intent, I download the file locally to my cache directory and return that.
My code has changed to this:
Intent i = new Intent(Intent.ActionView);
i.SetDataAndType(Android.Net.Uri.Parse("content://com.sample.erik.provider/files/" + id), mimeType);
i.SetFlags(ActivityFlags.GrantReadUriPermission);
i.SetFlags(ActivityFlags.NewTask);
i.SetFlags(ActivityFlags.ClearWhenTaskReset); // so if the app is relaunched, we don't show the display application.
StartActivity(i);
Then, I have my own content provider which does most of the work in OpenFile()
public override ParcelFileDescriptor OpenFile(Android.Net.Uri uri, string mode)
{
switch (sUriMatcher.Match(uri))
{
case FILE_ID:
if (mode != "r")
throw new Java.Lang.UnsupportedOperationException("Do not support write access: " + uri);
String id = uri.LastPathSegment;
Java.IO.File file = new Java.IO.File(Application.Context.CacheDir, id);
DownloadToFile(file, id);
return ParcelFileDescriptor.Open(file, ParcelFileMode.ReadOnly);
default:
throw new Java.Lang.IllegalArgumentException("Unknown Uri: " + uri);
}
}
It is not my original plan, but this way seems to work quite well and meets my needs.