In My Application I download PDF file to internal storage. after this I want to send mail with the file. I see the file is dowloaded in internal memory
com.my.app -> files-> pdffile.pdf
and it has permissions -rw-------
when I attach to mail the file and send gmail says: could't send attachment. But why ?? I have permission
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
this is code for dowloading file. it runs in async task
public static boolean saveFile(String fileName, Context context){
String fileDirectory = context.getFilesDir().getAbsolutePath()
+ "//"+fileName;
String urlServ = Constants.serverUrl+ "upload/forms/"+fileName;
urlServ = urlServ.replace(" ", "%20");
urlServ = urlServ.replace("\n", "%0d");
urlServ = urlServ.replace("\"", "%22");
int count;
URI fUri = URI.create("file://" + fileDirectory);
File f = new File(fileDirectory);
if (f.exists()){
f.delete();
}
try {
URL url = new URL(urlServ);
URLConnection conexion = url.openConnection();
conexion.connect();
int lenghtOfFile = conexion.getContentLength();
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(fileDirectory);
//OutputStream output = context.openFileOutput(fileDirectory, Context.MODE_PRIVATE);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {
return false;
}
return true;
}
And this is code for sending mail :
public static void sendMail(Context context, String filename) {
String fileDirectory = context.getFilesDir().getAbsolutePath()
+ "/"+filename;
File f = new File(fileDirectory);
Uri URI =Uri.fromFile(f);
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("message/rfc822");
i.putExtra(Intent.EXTRA_EMAIL , new String[]{"mytestmail#gmail.com"});
i.putExtra(Intent.EXTRA_SUBJECT, "subject of email");
i.putExtra(Intent.EXTRA_STREAM, URI);
i.putExtra(Intent.EXTRA_TEXT , "body of email");
try {
context.startActivity(Intent.createChooser(i, "Send mail..."));
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(context, "There are no email clients installed.", Toast.LENGTH_SHORT).show();
}
}
What is wrong ? it sands mail only with subject and text... maybe there is some permissions issue. How can I donwload file and give it full permissions
EDIT:
It is permission issue, because when I send file with different permission from the same directory, mail with attachment is being sent. -rw-rw-rw- with this permission
How Can i donwload file and set -rw-rw-rw- permission to it ???
I have solved the problem, if anyone faces the same issue. When opening OutputStream like this
it gives new file permission -rw-rw-rw. and other application(Gmail in this case) can use it.
OutputStream output = context.openFileOutput( fileName, Context.MODE_WORLD_READABLE);
The problem here is that although your app has permissions to read the file, the email application doesn't have permission to read the file. Since Jelly Bean, StrictMode has produced a warning when you try to share a File URI outside your application because this kind of problem can occur where the app you are sharing a file with does not have permission to access the file. It is advised to use a content:// URI when sharing files between apps instead of a file:// URI.
I'd suggest using the FileProvider class, which provides a relatively simple way to share your files using a content:// URI.
Related
I have an android apk expansion file and in there are some PDF's.
In the official documentation they access the files inside the .obb via Inputstream. I am able to access the files inside the .obb via the inputstream.
Now I want to attach one of the files to an email with Intent. The E-Mail Intent works perfectly fine with files from the assets, so the problem is attaching the Inputstream.
How can I attach the PDF into the mail directly from the .obb?
Solved it!
You have to convert the Inputstream to a Temorary File, get the Uri of that file and attach it to the email Intent.
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("application/pdf");
try {
ZipResourceFile expansionFile = new ZipResourceFile("Path to .obb file");
InputStream fileStream = expansionFile.getInputStream("Path inside .obb");
String downloadordner = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString(); //used for temp storage
File tempFile = new File(downloadordner+"/"+"filename.pdf");
tempFile.deleteOnExit();
FileOutputStream out = new FileOutputStream(tempFile);
IOUtils.copy(fileStream, out);
Uri theUri = Uri.fromFile(tempFile);
i.putExtra(Intent.EXTRA_STREAM, theUri);
startActivity(Intent.createChooser(i, "PDF versenden..."));
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(preisliste.this, "Es wurde kein E-Mail Client gefunden.", Toast.LENGTH_SHORT).show();
}
catch (IOException e)
{
Log.v("Datei nicht gefunden","Main Expansion");
}
I'm trying to share an audio file from my res/raw folder. What I've done so far is:
Uri uri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.sound); //parse path to uri
Intent share = new Intent(Intent.ACTION_SEND); //share intent
share.setType("audio/*");
share.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(share, "Share sound to"));
When I choose to share it on GMail, for example, it says something like "Failed to attach empty file". Looks like I'm not getting the right file path, so I'm basically sharing nothing. What am I doing wrong?
Any help would be much appreciated.
Copy the audio file from the resource to external storage and then share it:
InputStream inputStream;
FileOutputStream fileOutputStream;
try {
inputStream = getResources().openRawResource(R.raw.sound);
fileOutputStream = new FileOutputStream(
new File(Environment.getExternalStorageDirectory(), "sound.mp3"));
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
fileOutputStream.write(buffer, 0, length);
}
inputStream.close();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_STREAM,
Uri.parse("file://" + Environment.getExternalStorageDirectory() + "/sound.mp3" ));
intent.setType("audio/*");
startActivity(Intent.createChooser(intent, "Share sound"));
Add WRITE_EXTERNAL_STORAGE permission to AndroidManifest.xml file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
What am I doing wrong?
Few apps handle android.resource Uri values correctly. Your choices are:
Drop the feature, or
Copy the data from the resource into a file, then use FileProvider, perhaps in conjunction with my LegacyCompatCursorWrapper, or
Use my StreamProvider, which can serve raw resources directly, or
Copy the data from the resource into a file, then use Uri.fromFile(), but this looks like it will stop working with the next version of Android, based on preliminary results from testing with the N Developer Preview
EDIT: It was causing a NullPointException. This is what was I doing:
File dest = Environment.getExternalStorageDirectory();
InputStream in = getResources().openRawResource(R.raw.sound);
try
{
OutputStream out = new FileOutputStream(new File(dest, "sound.mp3"));
byte[] buf = new byte[1024];
int len;
while ( (len = in.read(buf, 0, buf.length)) != -1){
out.write(buf, 0, len);
}
in.close();
out.close();
}catch (Exception e) {}
final Uri uri = FileProvider.getUriForFile(Soundboard.this, "myapp.folagor.miquel.folagor", dest); //NullPointerException right here!!
final Intent intent = ShareCompat.IntentBuilder.from(Soundboard.this)
.setType("audio/*")
.setSubject(getString(R.string.share_subject))
.setStream(uri)
.setChooserTitle(R.string.share_title)
.createChooserIntent()
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
The code was just fine. The only problem was that on the Manifest's permisions, I had "WRITE_EXTERNAL_STORAGE" instead of "android.permissions.WRITE_EXTERNAL_STORAGE". So I was not having permision to write in the external storage, which caused a FileNotFoundException due to the lack of permision. Now it works fine!
I'm maintaining a legacy Android app that uses OrmLiteSqliteOpenHelper to create a Sqlite database on the device. On the device, I found out this file lives at:
/data/data/[package-name]/databases/mydatabase.db
My app has an "E-mail Support" feature, and I have unfortunately been tasked with attaching this SQLite file to an e-mail Intent for troubleshooting user issues. I'm running into some permission problems. Here is the code I'm using:
public void email(String[] to, String subject ) {
Intent email = new Intent(Intent.ACTION_SEND);
email.setType("*/*");
email.putExtra(android.content.Intent.EXTRA_EMAIL, to);
email.putExtra(android.content.Intent.EXTRA_SUBJECT, subject );
File file = activity.get().getApplicationContext().getDatabasePath("mydatabase.db");
if( file.exists() )
{
if( file.canRead() )
{
Uri uri = Uri.fromFile(file);
email.putExtra(Intent.EXTRA_STREAM, uri);
activity.get().startActivity(Intent.createChooser(email, "Email DB File"));
}
}
}
When I run this code and choose "Gmail" as my mail client, I get a toast on the e-mail client saying "Permission denied for attachment".
Why does this happen? How can I grant permission to Gmail on this file? Any help is appreciated. Thanks.
I have found a workaround for this, but I'm still open to other solutions.
If I create a temporary copy of the file on SD card using Environment.getExternalStorageDirectory(), the Gmail client has permission to read this file. Mail client seems to only have permission issues accessing internal storage.
File file = activity.get().getApplicationContext().getDatabasePath("mydatabase.db");
if( file.exists() && file.canRead() )
{
try {
//We need to make a local copy of the file to SDCard, so Gmail can use it
File destination = new File(Environment.getExternalStorageDirectory(), "database_copy.db");
this.copy(file, destination);
//Attach file and send
Uri uri = Uri.fromFile(destination);
email.putExtra(Intent.EXTRA_STREAM, uri);
activity.get().startActivity(Intent.createChooser(email, "Email DB File"));
}
catch(IOException ioe){
return;
}
}
....
//File copy routine
private void copy(File src, File dst) throws IOException {
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
}
I'm downloading a PDF from my server.
The server send me a HttpResponse with the InputStream of file's body.
I'm able to write it into a file but, when I try to read it with a PDF reader, it tells me that the file might be corrupted.
I've also noticed that the size of the PDF downloaded directly from web service is twice the size of the PDF downloaded via my application.
The code I use to download and write the PDF file is this:
String fileName = //FILENAME + ".pdf";
fileName = fileName.replaceAll("/", "_");
String extPath = Environment.getExternalStorageDirectory().toString();
String folderName = //FOLDERNAME;
try {
File folder = new File(extPath, folderName);
folder.mkdir();
File pdfFile = new File(folder, fileName);
pdfFile.createNewFile();
URL url = new URL(downloadURL);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.connect();
InputStream inputStream = urlConnection.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream(pdfFile);
byte[] buffer = new byte[MEGABYTE];
int bufferLength;
while((bufferLength = inputStream.read(buffer))>0 ){
fileOutputStream.write(buffer, 0, bufferLength);
}
fileOutputStream.close();
Uri path = Uri.fromFile(pdfFile);
Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
pdfIntent.setDataAndType(path, "application/pdf");
pdfIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
startActivity(pdfIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(getApplicationContext(), "No Application available to view PDF", Toast.LENGTH_SHORT).show();
}
} catch (IOException e) {
e.printStackTrace();
}
//otherStuff
Where I go wrong?
I've also noticed that inside the Headers of HttpResponse contains Content-type:text/html. It shoudld be something like text/pdf?
Your Downloading code seems correct. Based on that and on your comment:
I've also noticed that the size of the PDF downloaded directly from web service is twice the size of the PDF downloaded via my application."
I would suggest checking your URL. It appears that you might be downloading an html page instead of the pdf. To verify you are downloading correctly, change the download directory as follows:
//Default download directory
String extPath = Environment.DIRECTORY_DOWNLOADS;
And check the directory (via the file system, e.g. mount the phone to your computer or a file manager app) for the downloaded content to verify it is a pdf.
I am using following code to download and read a PDF file from internal storage on device.
I am able to download the files successfully to the directory:
data/data/packagename/app_books/file.pdf
But I am unable to read the file using a PDF reader application like Adobe Reader.
Code to download file
//Creating an internal dir;
File mydir = getApplicationContext().getDir("books", Context.MODE_WORLD_READABLE);
try {
File file = new File(mydir, outputFileName);
URL downloadUrl = new URL(url);
URLConnection ucon = downloadUrl.openConnection();
ucon.connect();
InputStream is = ucon.getInputStream();
FileOutputStream fos = new FileOutputStream(file);
byte data[] = new byte[1024];
int current = 0;
while ((current = is.read(data)) != -1) {
fos.write(data, 0, current);
}
is.close();
fos.flush();
fos.close();
isFileDownloaded=true;
} catch (IOException e) {
e.printStackTrace();
isFileDownloaded = false;
System.out.println(outputFileName + " not downloaded");
}
if (isFileDownloaded)
System.out.println(outputFileName + " downloaded");
return isFileDownloaded;
Code to read the file
PackageManager packageManager = getPackageManager();
Intent testIntent = new Intent(Intent.ACTION_VIEW);
testIntent.setType("application/pdf");
List list = packageManager.queryIntentActivities(testIntent,
PackageManager.MATCH_DEFAULT_ONLY);
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
File fileToRead = new File(
"/data/data/com.example.filedownloader/app_books/Book.pdf");
Uri uri = Uri.fromFile(fileToRead.getAbsoluteFile());
intent.setDataAndType(uri, "application/pdf");
startActivity(intent);
} catch (Exception ex) {
Log.i(getClass().toString(), ex.toString());
Toast.makeText(MainActivity.this,
"Cannot open your selected file, try again later",
Toast.LENGTH_SHORT).show();
}
All works fine but the reader app says "File Path is not valid".
Your path is only valid for your app. Place the file in a place where other apps can 'see' it. Use GetExternalFilesDir() or getExternalStorageDirectory().
Note about files which are created inside the directory created by Context.getDir(String name, int mode) that they will only be accessible by your own application; you can only set the mode of the entire directory, not of individual files.
So you can use Context.openFileOutput(String name, int mode). I'm re-using your code for an example:
try {
// Now we use Context.MODE_WORLD_READABLE for this file
FileOutputStream fos = openFileOutput(outputFileName,
Context.MODE_WORLD_READABLE);
// Download data and store it to `fos`
// ...
You might want to take a look at this guide: Using the Internal Storage.
If you would like to keep the file app specific, you can use PdfRenderer available for Lollipop and above builds. There are great tutorials on google and youtube that work well. The method you are using is a secure way to store a PDF file that is only readable from inside the app ONLY. No outside application like Adobe PDF Reader will be able to even see the file.It took me a lot of seaching but I found a solution to my specific usage by using this site and especially youtube.
How to download PDF file from asset folder to storage by making folder
make sure you have storage permission are given like marshmallow device support etc then follow these steps
private void CopyReadAssets()
{
AssetManager assetManager = getContext().getAssets();
FileInputStream in = null;
FileOutputStream out = null;
File sdcard = Environment.getExternalStorageDirectory();
File dir = new File(Environment.getExternalStorageDirectory()+File.separator+ "A_level");
File dir2;
if (dir.exists() && dir.isDirectory()){
Log.e("tag out", ""+ dir);
}else {
dir.mkdir();
Log.e("tag out", "not exist");
}
File file = new File(dir, mTitle+".pdf");
try
{
Log.e("tag out", ""+ file);
out = new FileOutputStream(file);
in = new FileInputStream (new File(mPath));
Log.e("tag In", ""+ in);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e)
{
Log.e("tag out", ""+ out);
Log.e("tag In", ""+ in);
Log.e("tag", e.getMessage());
Log.e("tag", ""+file);
Log.i("tag",""+sdcard.getAbsolutePath() + "A_level");
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException
{
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
}