I created a file with
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(FILE_TYPE); // text/*
intent.putExtra(Intent.EXTRA_TITLE, "vocable_export.csv");
startActivityForResult(intent, CREATE_FILE);
// writing to received URI
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(context.getContentResolver().openOutputStream(es.file, "w");));
Now when I try to select that file again, I get the file I created greyed out, such that I can't select it. I tried killing the app in case I didn't close the file correctly, but that didn't help. I can select & open files dragged into the emulator perfectly fine. What does work is renaming the file so it doesn't contain the (1) at the end any more.
Open code
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(FILE_TYPE); // text/*
startActivityForResult(intent, PICK_FILE);
Android simply seems to fail displaying such files when a (1) is appended on writing a second file with the same name. Renaming such files will get them to show up again in a selectable state.
I'm trying to open a pdf after creating it via an intent. The file exists and is readable but save inside the apps directory.
Currently the file is saved in the following manner
OutputStream out;
try {
//TODO: expose through a content provider
out = mContext.openFileOutput(outputFileName, Context.MODE_WORLD_READABLE);
document.writeTo(out);
out.close();
and the file is sent to an intent with the following code
final Intent viewIntent = new Intent();
viewIntent.setAction(Intent.ACTION_VIEW);
viewIntent.setDataAndType(Uri.fromFile(file), "application/pdf");
mActivity.startActivity(viewIntent);
The app trying to open it is the google drive viewer and I see in the Android monitor
E/PdfViewerActivity: fetchFile:file: java.io.FileNotFoundException: file does not exist
This previously worked and I don't think I've changed anything related to this code however I have updated my tooling. I've tried changing the buildToolsVersion in the build.gradle back to what it was before as well as the support libraries 'com.android.support:appcompat-v7:22.+' and 'com.android.support:design:22.+' instead of version 24.2.1
I've tried using the debugger to set the file to be readable just before sending the intent but that did not work.
Thanks
I found the answer starting with the Android 7 Behavior Changes documentation. Sharing files is no longer allowable when setting MODE_WORLD_READABLE even though the documentation doesn't mention any of this
I followed the documentation on sharing files to fix the issue but the sample code in the documentation was more complicated than what I needed. I still had to add the file provider to the AndroidManifest.xml as well as create the fileprovider.xml.
The resulting code is now
final Intent viewIntent = new Intent();
viewIntent.setAction(Intent.ACTION_VIEW);
Uri fileUri = FileProvider.getUriForFile(mActivity, "my.authority.fileprovider", file);
viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
viewIntent.setDataAndType(fileUri, "application/pdf");
I'm working on a functionality for an Android-Application, which downloads a file from a local server using the Android Download-Manager. Up to now I easily download the file to the downloads directory by using
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,filename);
Now I would like to let the user choose the target including the filename. Therefore I use the ACTION_CREATE_DOCUMENTIntent:
void pickTargetFile() {
Intent intent = new Intent()
.setAction(Intent.ACTION_CREATE_DOCUMENT)
.setType("image/jpeg");
startActivityForResult(Intent.createChooser(intent, "Select a target to save the file"), SAVE_FILE_CODE);
}
But when I now use the resulting path in the download-manager it doesn't write the data in the created file but creates a new one named e.g. filename-1.jpeg when filename.jpeg was created by the File-Create-Dialog.
Either I would like the download-manager to write into the previously created file or use another dialog to choose a target for the download-manager but not creating the file by the dialog.
Does someone know an easy way to archive this?
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);
I want to programatically launch a default file explorer to show me the contents of a folder.
I'm using this code, but it crashes:
Uri startDir = Uri.fromFile(new File("/sdcard/DCIM/Camera"));
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(startDir);
startActivity(intent);
LogCat shows "No Activity found to handle the Intent"...
What's my best option to do this? I want the user to see the contents of the folder and be able to click the files (e.g. click a video and launch it with default player, click a PDF and open it etc).
Unfortunately there seem to be no standard way to do this, I was searching for the exact same thing before and couldn't find out any solution. There are 2 alternative methods that might work for you:
1) Use a general intent and let the user pick his/her file manager
This is safe and easy but a little far from what we really want
Uri startDir = Uri.fromFile(new File(Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/DCIM/Camera"));
Intent intent = new Intent();
intent.setData(startDir);
intent.setType("*/*");
intent.setAction(Intent.ACTION_VIEW);
startActivity(intent);
Don't use intent.setType("file/*");, it's not a standard MIME type.
2) Use Specific Intents that the famous file managers provide The well known file managers have their own custom intent filters that accept a directory path which allows simple browsing. Some of them are here: OI file manager, ES Explorer
Maybe you could check if the user have these specific file managers installed and then use this solution, else fallback to general intent.
For now these are the only two options you have. I will update this post if I find out any better solution.
Although I could not get the OI file manager to go to a specific directory, the following opens up the OI File Manager directly and goes to the last directory it was in.
Intent importFileIntent = new Intent();
importFileIntent.setType( "file/*" );
// Does nothing.
// Uri startingDir = Uri.fromFile( new File(
// Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera"));
// importFileIntent.setData( startingDirectory );
importFileIntent.setAction( Intent.ACTION_GET_CONTENT );
// Ensure that there's an activity to handle the intent.
if (importFileIntent.resolveActivity(getPackageManager()) == null) return;
startActivityForResult(importFileIntent, REQUEST_FILE_IMPORT);
If anybody has further success, I would love to hear it.