E/PdfViewerActivity: fetchFile:file: java.io.FileNotFoundException: file does not exist - android

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");

Related

Error in creating a folder using Storage Access Framework

I am using Storage Access Framework and want to create a folder (location choosed by user through file picker) but instead it is creating a file without any extension.
I am using ACTION_CREATE_DOCUMENT for this, you can check the intent call below:
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.setType("application/vnd.google-apps.folder");
intent.putExtra(Intent.EXTRA_TITLE, "CCM-Tele ICU");
startActivityForResult(intent, MAKE_DIRECTORY_REQUEST_ID);
But instead of folder it is creating this:
I have tried creating files and it works fine but unfortunately, folder is not being created.
I solved the problem of directory creation so answering my own question maybe it will help someone in future.
The Mime type that I was using is incorrect. So change it to: DocumentsContract.Document.MIME_TYPE_DIR
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.setType(DocumentsContract.Document.MIME_TYPE_DIR);
intent.putExtra(Intent.EXTRA_TITLE, "CCM-Tele ICU");
startActivityForResult(intent, MAKE_DIRECTORY_REQUEST_ID);
But it's useless as I can't use it to store things afterwards using SAF. So it's better to use ACTION_OPEN_DOCUMENT_TREE instead

Android default file browser intent showing only *.json files

My goal is to list only *.json files in android filebrowser, to load them to the software.
File folder = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + PATH_TO_WRITE);
Uri uri = Uri.fromFile(folder);
intent.setDataAndType(uri, "file/.json");
intent.addCategory(Intent.CATEGORY_OPENABLE);
Activity activity = (Activity) context;
activity.startActivityForResult(intent, FILECHOOSER_RESULTCODE);
Looks like the Android filebrowser dosen't support *.json files. Is there way to implement FileFilter to the intent?
Have you tried using
intent.setDataAndType(uri, "application/json");
Edit:
It appears although this currently isn't supported in the default android file explorer (Please see this)
Sovled this by using custom made file browser because android does not support *.JSON formats. Used this library https://github.com/adityak368/Android-FileBrowser-FilePicker , which was easy to implement

Android Attaching a file to GMAIL - Can't attach empty file

I had a program that would always attach the same file to GMAIL (Compose > Attach File > Open From > "MyProgram"). It would always select the same file.
What it was doing was:
String path = Environment.getExternalStorageDirectory() + "/file.3gp";
File f = new File(path);
Uri data = Uri.fromFile(f);
Intent i = new Intent();
i.setData(data);
setResult(Activity.RESULT_OK, i);
finish();
This was working fine until Android 6.0.
Now, I receive the following error when trying to use it:
Can't attach empty file
Astro File Sharing is giving me the same error (can be an old build).
However, I installed ES File Explorer, and when I do the same routine, and select the file, I receive a Dialog which says:
Pick up file as
Normal Android Way (For MMS,Gmail,...)
File Way (Try this if above fails)
The "File Way" will fail as my program does. The "Normal Android Way" will work fine.
Does anyone have any idea on what it does, so I can replicate?
Thanks in advance!
OBS: Already tried the putExtra(STREAM, path) a lot of times, but without success.
Ok, got it to work now, after a lot of research and intercepting some Intents.
What I had to do was change the file:/// to content://.
I did this following this information from Android: https://developer.android.com/reference/android/support/v4/content/FileProvider.html
The only major change was that I used a hard-coded path to /sdcard/file.ext.
Also, the line
getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
was changed to
Uri contentUri = FileProvider.getUriForFile(this, "com.mydomain.fileprovider", newFile);
Also had to include:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
i.setData(contentUri);
I do not really understand why I had to change from File to Content, but after this, the file is now being attached again! See the link if you face this issue, and don't forget about the new .xml that needs to be created.
See the following question:
android-6-cannot-share-files-anymore
This behavior is with Android 6.0 and Gmail. Please see the following thread.
Issue 3141 - android-developer-preview
If you go to Settings->Apps->Gmail->Permissions and enable the
"Storage" permission manually, then the share works.
Gmail should ask for "Storage" permission in this scenario and it
would work as it did in all the past version of Android.
Other email apps should handle attachments correctly.
Here is how to fix it.
Go to Settings -> Apps -> Gmail -> Permissions
Turn on the permission for "Storage"
That work-around solved the issue for me.
I couldn't find a clear answer(sending an attachment on gmail without SD card) I tried to copy to another file name but no cigar. The way I got it to work was copy to the Downloads folder and go from there.
Get the path for Downloads with this
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
pulls the full path to downlaods folder copy the attachemnt to there and we are good. Files over 20M don't go
Share any file using INTENT provided given file path
//File file= shareable File Path
Uri uri = FileProvider.getUriForFile(this, "com.example.myapp.fileprovider", file);
//FileProvider authorities from Manifest
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.setType("*/*");
sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(sharingIntent);
Just match your authorities from the manifest in arguments of method getUriForFile.
In Manifest
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>

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);

send file using default bluetooth application in android

I want to send a file from my phone to other phone using my Android application. The File is of type ".Xcard". I want to make use of the default Bluetooth Application provided by the android environment. That is, when I click on send , the default application chooser should open and then I should be able to send the File to other device bu selecting the device.
How should I do it? The file may or may not be empty
I have tried the following code but its not working. I get a toast message saying :File was not sent"
f = File.createTempFile("card", ".Xcard", getCacheDir());
FileWriter fw = new FileWriter(f);
BufferedWriter w = new BufferedWriter(fw);
w.write("hello my name is neeraj");
w.close();
Intent i = new Intent();
i.setAction(Intent.ACTION_SEND);
i.setType("*/*");
i.putExtra(i.EXTRA_STREAM, Uri.fromFile(f));
startActivity(i);
Please help me out, I am kind of Stuck
From the API docs:
public abstract File getCacheDir ()
Returns the absolute path to the application specific cache directory on the filesystem.
(emphasis mine)
The directory returned by getCacheDir() is only readable by your application, use getExternalCacheDir() instead.

Categories

Resources