I have upgraded to android 11. I am having an issue downloading PDF files.
I have used this code:
private void createFile(Uri pickerInitialUri, String title) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
intent.putExtra(Intent.EXTRA_TITLE, title);
// Optionally, specify a URI for the directory that should be opened in
// the system file picker when your app creates the document.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
}
startActivityForResult(intent, CREATE_FILE);
}
The file is created but the file is empty. I am still unable to save the downloaded pdf file.
I used to use DownloadManager request to download the pdf file from web.
DownloadManager downloadManager = (DownloadManager) this.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(uri);
if (SDK_INT > Build.VERSION_CODES.Q) {
// Uri uri1 = Uri.fromFile(new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "")); //before android 11 this was working fine
// Uri uri1 = Uri.fromFile(new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), ""));
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(true).setTitle(title + strDate + ".pdf")
.setDescription(description)
//.setDestinationUri(uri1) // before android 11 it was working fine.
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, title + strDate + ".pdf") // file is not saved on this directory.
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);//to show the DOWNLOAD notification when completed
// createFile(uri , title + strDate + ".pdf"); // for new scoped storage
} else {
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(true).setTitle(title + strDate + ".pdf")
.setDescription(description)
.setDestinationInExternalPublicDir(FileUtils.downloadPdfDestination(), title + strDate + ".pdf")
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //to show the DOWNLOAD notification when completed
}
long PDF_DOWNLOAD_ID = downloadManager.enqueue(request);```
ACTION_CREATE_DOCUMENT is used to create a new document. If one already existed, it will be overwritten. If you want to view an existing document, use ACTION_VIEW.
Of course none of the code you posted actually downloads a PDF. If you need help with that, post your DownloadManager code.
Check this code snippet:
override fun startDownload(url: String, onError: (e: Exception) -> Unit) {
try {
val request = DownloadManager.Request(Uri.parse(url))
request.setDestinationInExternalPublicDir(
Environment.DIRECTORY_DOWNLOADS,
UUID.randomUUID().toString()
)
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION)
(context.getSystemService(DOWNLOAD_SERVICE) as DownloadManager).enqueue(request)
} catch (e: Exception) {
e.printStackTrace()
onError.invoke(e)
}
}
It's working fine on Android 11 by using DownloadManger API.
Use below code to download & view pdf.
First you need to apply rxjava dependency for background task.
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
Don't forgot to check WRITE_EXTERNAL_STORAGE permission before call below method. Also check INTERNET permission as well.
Then use below method to perform operation in background.
private void downloadAndOpenInvoice() {
mDialog.show();
Observable.fromCallable(() -> {
String pdfName = "Invoice_"+ Calendar.getInstance().getTimeInMillis() + ".pdf";
String pdfUrl = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
File file = CommonUtils.downloadFile(mActivity, pdfUrl, pdfName,mDialog);
return file;
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(file -> {
CommonUtils.viewPdf(file, mActivity, mDialog);
});
}
To download file from url use below snippet
public static File downloadFile(Activity mActivity, String url, String fileName, CustomDialog mDialog) {
// write the document content
File fileDir = new File(CommonUtils.getAppDir(mActivity, "Invoice")); //Invoice folder inside your app directory
if (!fileDir.exists()) {
boolean mkdirs = fileDir.mkdirs();
}
File pdfFile = new File(CommonUtils.getAppDir(mActivity, "Invoice"), fileName); //Invoice folder inside your app directory
try {
URL u = new URL(url);
URLConnection conn = u.openConnection();
int contentLength = conn.getContentLength();
DataInputStream stream = new DataInputStream(u.openStream());
byte[] buffer = new byte[contentLength];
stream.readFully(buffer);
stream.close();
DataOutputStream fos = new DataOutputStream(new FileOutputStream(pdfFile));
fos.write(buffer);
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
if (mDialog.isShowing()) {
mDialog.dismiss();
}
Toast.makeText(mActivity, "Something wrong: " + e.toString(), Toast.LENGTH_LONG).show();
}
return pdfFile;
}
for app directory
public static String getAppDir(Context context, String folderName) {
return context.getExternalFilesDir(null).getAbsolutePath() + File.separator + folderName + File.separator;
}
Use below code to view pdf
public static void viewPdf(File pdfFile, Activity mActivity, CustomDialog mDialog) {
Uri uri = FileProvider.getUriForFile(mActivity, mActivity.getApplicationContext().getPackageName() + ".provider", pdfFile);
// Setting the intent for pdf reader
Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
pdfIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
pdfIntent.setDataAndType(uri, "application/pdf");
//pdfIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
if (mDialog.isShowing()) {
mDialog.dismiss();
}
}
});
mActivity.startActivity(pdfIntent);
Log.e("Invoice - PDF", pdfFile.getPath());
} catch (ActivityNotFoundException e) {
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
if (mDialog.isShowing()) {
mDialog.dismiss();
}
}
});
e.printStackTrace();
Log.e("Invoice - PDF", "Can't read pdf file");
Toast.makeText(mActivity, "Can't read pdf file", Toast.LENGTH_SHORT).show();
}
}
Related
I have a video that plays on my application from a server. I want to make it possible to share the video to WhatsApp when the shared button is clicked.
I understand that I have to download load the video to the device storage before sharing through Intent. Below is my code, when the shared button is click the progress bar keeps loading and nothing else happens, I'm also using FileProvider. How do I go about this?
The getVideoUrl() method below takes in the video url and returns the Uri (file path) after the video has been downloaded
private Uri getVideoUrl(String fileURL) {
Uri videoUri = null;
try {
File rootFile = new File(getCacheDir(), "share_video_" + System.currentTimeMillis() + ".mp4");
URL url = new URL(fileURL);
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.connect();
FileOutputStream f = new FileOutputStream(rootFile);
InputStream in = c.getInputStream();
byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = in.read(buffer)) > 0) {
f.write(buffer, 0, len1);
}
videoUri = FileProvider.getUriForFile(this,
getPackageName() + ".provider", rootFile);
f.close();
} catch (IOException e) {
Log.d("Error....", e.toString());
}
return videoUri; // returns the file path to the video from storage
}
method to share the video,which is called is onClick of the share button. Then I receive the resultCode in onActivityResult() and make the progressBar invisible and display a message that the video has been shared.
public void shareVideo(String videoUrl, String desc){
progressBar.setVisibility(View.VISIBLE);
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("*/*");
i.setPackage("com.whatsapp");
i.putExtra(Intent.EXTRA_TEXT, desc ); //to share text
i.putExtra(Intent.EXTRA_STREAM, getVideoUrl(videoUrl)); //to share video
i = Intent.createChooser(i, "Share video");
startActivityForResult(i, POSTED_VIDEO);
}
shareVideo() is now called in the shareButtonOnClickListner()
#Override
public void onClick(View v) {
String videoUrl = "https://linktoVideo.mp4"; //just an example link
String desc = "Shared Video;
switch (v.getId()) {
case R.id.post_image:
shareVideo(videoUrl, desc);
}
}
What am I doing wrongly?
To send the email the method for the button is;
public void buttonSendEmailClicked(View view) {
File file = saveFileToShare();
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("application/image");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{});
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,"Check Out MyPic");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, "Taken With Android!");
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
startActivityForResult(Intent.createChooser(emailIntent, "Send mail..."), interstitial_request);
}
The saveFileToShare element is this;
public File saveFileToShare() {
try
{
File fileImage = new File(Environment.getExternalStorageDirectory() + "/DCIM/Camera/attachment.png");
if(!fileImage.exists())
{
fileImage.delete();
}
editorImage.setDrawingCacheEnabled(true);
Bitmap bitmap = editorImage.getDrawingCache();
fileImage.createNewFile();
FileOutputStream ostream = new FileOutputStream(fileImage);
bitmap.compress(CompressFormat.PNG, 100, ostream);
ostream.close();
editorImage.invalidate();
editorImage.setDrawingCacheEnabled(false);
return fileImage;
}
catch (Exception e)
{
System.out.print(e);
e.printStackTrace();
return null;
}
}
Saving the image works fine, the save code is;
public void buttonSaveImageClicked(View view) throws IOException {
editorImage.setDrawingCacheEnabled(true);
Bitmap bitmap = editorImage.getDrawingCache();
SaveLayoutToFile saveImage = new SaveLayoutToFile(this, bitmap, editorImage);
String filePath = Environment.getExternalStorageDirectory() + "/DCIM/Camera/wonkydog";
saveImage.execute(filePath);
}
I need to set the email code to grab the image and attach to email.
At the moment when I press the email button it just returns to the title screen without doing anything else.
If I comment out this line
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
Then it opens the mail send dialogue, but without attachment of course...
I found the answer, rather than using the emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
I replaced it with;
emailIntent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(EditorActivity.this, "com.myapp.myappname.provider", file));
It now works correctly!
I have been able to make users upload photos to server taken from their gallery, but when a user uses camera to capture live and upload, I get this error unsupported scheme: file::///storage/...
I am using android upload service library
implementation "net.gotev:uploadservice:3.5.2"
I searched and discovered that file:// scheme is not allowed to be attached with Intent.
1. Selecting image from camera on button click
capture_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT,
FileProvider.getUriForFile(UploadActivity2.this, BuildConfig.APPLICATION_ID + ".provider",
createImageFile()));
startActivityForResult(intent, 0);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
2. Getting the image captured
if (resultCode == Activity.RESULT_OK)
switch (requestCode){
case 0:
try {
Uri cameraPath = FileProvider.getUriForFile(UploadActivity2.this,
BuildConfig.APPLICATION_ID + ".provider", createImageFile());
String stringUri = cameraPath.toString();
selectedImages.add(stringUri);
}catch (IOException ex) {
ex.printStackTrace();
Glide.with(this)
.load(cameraFilePath)
.into(display_image);
break;
}
}
As you see, I am using file provider to get Uri and storing the uri in a variable called selectedimages so I can pass it through an intent to another activity where the upload occurs
createImageFile method
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.UK).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
//This is the directory in which the file will be created. This is the default location of Camera photos
File storageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM), "Camera");
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for using again
cameraFilePath = "file://" + image.getAbsolutePath();
return image;
}
3. Passing intent PATH, to uploadActivity
next_upload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(UploadActivity2.this, UploadActivity3.class);
intent.putStringArrayListExtra("PATH", selectedImages);
startActivity(intent);
}
});
5. UploadAcvity where the upload happens
public void upload() {
ArrayList<String> selectedImages = getIntent().getStringArrayListExtra("PATH");
try {
String uploadId = UUID.randomUUID().toString();
//Creating a multi part request
new MultipartUploadRequest(this, uploadId, EndPoints.UPLOAD_URL)
.addFileToUpload(selectedImages.get(0), "image") //Adding file
.addParameter("caption", captionx) //Adding text parameter to the request
.setNotificationConfig(new UploadNotificationConfig())
.setMaxRetries(0)
.startUpload(); //Starting the upload
} catch (Exception exc) {
Toast.makeText(this, exc.getMessage(), Toast.LENGTH_SHORT).show();
}
}
Using this.
a. The image is not displaying in point 2 using Glide.with(this)
.load(cameraFilePath)
.into(display_image);
b. The image uploads but it is an empty file of 0 bytes.
But when I change the value of the variable of selectedImages in point 2 from selectedImages.add(stringUri); to selectedImages.add(cameraFilePath);
b. After clicking upload, I get error unsupported scheme: file::///storage/
Found the answer.
1. In the createImageFile method. I had to get the coontent:// url from the captured file using -
File newFile = new File(storageDir, image.getName());
contentUrl = FileProvider.getUriForFile(UploadActivity2.this,
BuildConfig.APPLICATION_ID + ".provider", newFile);
stringContentUrl = contentUrl.toString();
2. Then I passed the stringContentUrl into selected images String while getting the image captured.
selectedImages.add(stringContentUrl);
3. Passed it through an intent Extra and called it in the uploadActivity as seen in point 3 and 4 in the question.
I am trying to create file in internal storage and then send it via email in Android.
However, I still get file not found or similar errors.
Please, help!
String FILENAME = "TestFile.txt";
Sending file by button click
btnSendFile.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
File gpxfile = getFile();
Uri path = Uri.fromFile(gpxfile);
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("message/rfc822");
Context context = v.getContext();
String email = "MYEMAILHERE";
i.putExtra(Intent.EXTRA_EMAIL, new String[]{email});
i.putExtra(Intent.EXTRA_SUBJECT, "Subject");
i.putExtra(Intent.EXTRA_TEXT, "Text");
i.putExtra(Intent.EXTRA_STREAM, path);
context.startActivity(Intent.createChooser(i, context.getString("Sending...")));
}
});
}
Creating file in internal storage
private void createTestFile() {
try {
FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_APPEND);
fos.write("Your content".getBytes());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Get file function. Return File if file exists
private File getFile() {
return new File(getFilesDir() + "/" + FILENAME);
}
Maybe you need to make
Context.openFileOutput(FILENAME, Context.MODE_APPEND);
Instead of that one you used? I think someone had similiar problem over here: android what is wrong with openFileOutput?
The requirements: ensure that the PDF document is deleted from the device after the user has left the PDF viewing screen
The problem: on certain devices (Samsung 4.4.2 and Samsung 4.1.2 for sure, but not Asus 4.2.1) only the first time that the PDF is requested after restarting the application an error message is displayed stating "This document cannot be opened". Thereafter the PDF will load normally. I'm thinking this is a timing issue due to processes that need to be started the first time, but are running after the first attempted load.
The code: note that createFile() is called first, then startActivityForIntentResult()
private File file;
private ArrayList<Uri> uriList = new ArrayList<Uri>();
private void createFile() {
int fileNameLength = pdfFileName[0].length();
String fileName = pdfFileName[0].substring(0, fileNameLength - 4) + DateTime.now();
String fileExtension = pdfFileName[0].substring(fileNameLength - 4, fileNameLength);
byte[] content = Base64.decodeBase64(pdfData[0].getBytes());
BufferedOutputStream outputStream = null;
try {
File path = new File(getExternalFilesDir(null).getAbsolutePath(), "temp");
if (!path.exists()) {
path.mkdirs();
}
file = new File(path, fileName + fileExtension);
outputStream = new BufferedOutputStream(new FileOutputStream(file));
outputStream.write(content);
file.deleteOnExit();
uriList.add(Uri.fromFile(file));
}
catch (FileNotFoundException ex) {
ex.printStackTrace();
}
catch (IOException ex) {
ex.printStackTrace();
}
finally {
try {
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
private static int REQUEST_CODE = 1;
private Intent intent;
private void startActivityForIntentResult() {
if (file.exists()) {
Uri targetUri = uriList.get(0);
intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(targetUri, "application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
try {
startActivityForResult(intent, REQUEST_CODE);
}
catch (ActivityNotFoundException e) {
toastTitle = "Error Displaying PDF";
toastMessage = "Please make sure you have an application for viewing PDFs installed on your device and try again.";
toast = new GenericCustomToast();
toast.show(toastTitle, toastMessage, QueryForPDF.this);
}
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (resultCode == RESULT_CANCELED && requestCode == REQUEST_CODE) {
if(!file.delete()) {
file.delete();
}
}
searchAgain();
}
#Override
public void onBackPressed() {
super.onBackPressed();
if(!file.delete()) {
file.delete();
}
searchAgain();
}
#Override
public void onStop() {
super.onStop();
if(!file.delete()) {
file.delete();
}
}
#Override
public void onDestroy() {
super.onDestroy();
if(!file.delete()) {
file.delete();
}
}
EDIT: I have also tried implementing a callback to be absolutely certain that createFile() has finished it's work. I even tried adding delays (of different time increments) as well as adding (the completely unnecessary) flags for Intent.FLAG_GRANT_READ_URI_PERMISSION, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, and Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION.
I still don't know why this works, but here's the solution in case anyone else runs into this issue:
It's the directory where the file is created. For some reason on the two Samsung devices there was something different in how the files were either accessed or created versus the Asus device. So File path = new File(getExternalFilesDir(null).getAbsolutePath(), "temp"); becomes File path = new File(getExternalCacheDir().getAbsolutePath()); and the problem goes away.