Open File created by App in internal storage - android

I'm programming an app that receives all kind of documents as base64 strings. I've been searching all morning, and I didn't find a way to display the documents properly without storing them. As an alternative I wrote this:
private void createReadableFile(DocumentBinary document) {
try {
byte[] bytes = Base64.decode(document.getDocument(), 0);
FileOutputStream os = openFileOutput(document.getSuggestedFileName(), MODE_PRIVATE);
os.write(bytes);
os.flush();
os.close();
openFile(document);
} catch (Exception e) {
Ln.e(e, "Error while parsing document");
}
}
And I do this with the created file:
private void openFile(File file, String mimeType) {
Intent viewIntent = new Intent();
viewIntent.setAction(Intent.ACTION_VIEW);
viewIntent.setDataAndType(Uri.fromFile(file), mimeType);
viewIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(viewIntent);
}
But it doesn't start anything. It calls onPause and onResume but nothing happens. I know that if I change MODE_PRIVATE to MODE_WORLD_READABLE it would work, but MODE_WORLD_READABLE is deprecated. Do you know a better way to do it or what to use instead of MODE_PRIVATE?

Save the file to external storage, or
Use FileProvider to create a ContentProvider that serves the file from internal storage, or
Create your own ContentProvider that implements openFile() and serves up a stream of content from some other source

Related

[Android ]Intent.ACTION_VIEW - Not found

I am having an issue, I have never had problem opening files via ACTION_VIEW the next way:
File file = new File(getActivity().getFilesDir(), TEMP_FILE_NAME);
String dataType = "image/*";
if (file.exists()) {
Intent fileIntent = new Intent(Intent.ACTION_VIEW);
fileIntent.setDataAndType(Uri.fromFile(file), dataType);
fileIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
Intent intent = Intent.createChooser(fileIntent, "Open file");
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Log.e(TAG, "There is a problem when opening the file");
}
} else {
Toast.makeText(getContext(), "Invalido", Toast.LENGTH_LONG).show();
}
The problem I am having right now is that even though the file exists when I choose the app to open the file it immediately closes and tells me Not found. I have put the image I am loading in an image view and there is no problem, so the file is valid but for some reason it has conflicts when I am opening it via intent.
I am aware that it may have something to do with the way I am creating the file, I am retrieving it from Google drive so I am writing the file using the Apache Commons library the next way:
DriveContents contents = result.getDriveContents();
InputStream inputStream = contents.getInputStream();
File file = new File(getActivity().getFilesDir(), TEMP_FILE_NAME);
try {
OutputStream outputStream = new FileOutputStream(file);
IOUtils.copy(inputStream, outputStream);
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
} catch (IOException e) {
e.printStackTrace();
}
What is it I am doing wrong? I am not totally sure if the problem has to do with the copy method executing asynchronously or something like that.
Thanks in advance.
I have never had problem opening files via ACTION_VIEW the next way
That code will never work, as third-party apps have no rights to work with files on getFilesDir() of your app.
What is it I am doing wrong?
You are attempting to serve an inaccessible file to third-party programs. Use FileProvider to serve the file, using FileProvider.getUriForFile() to get the Uri to use in your ACTION_VIEW Intent.

I am working on an android app that need to create a text file and write in it. I use the following code

I am working on an android app that need to create a text file and write in it. I use the following code:
public void onButtonClick(View view) {
writeInFile("Hello world");
}
public void writeInFile(String string) {
String dir = android.os.Environment.getExternalStorageDirectory().getAbsolutePath();
File file = new File(dir, "MyFile.txt");
try {
FileWriter fw = new FileWriter(file,true);
//fw.append(string);
fw.write(string);
Toast.makeText(this, "You wrote in the file", Toast.LENGTH_SHORT).show();
} catch(Exception e){
e.printStackTrace();
}
}
When I run the app, the file is created, I can see the toast message, but nothing in written in it. I have tried fw.append and fw.write. In my manifest I have yet written the necessary permission to write on external storage. I want to write directly in the external storage of my Samsung Galaxy Tab 4 (Marshmallow), not on the SD card.
First, always close(). You are not closing your FileWriter.
Second, always flush(). You are not flushing your FileWriter.
Third, even if you flush() and close(), due to the way buffered filesystems work, the bytes may not yet be written to disk. You're flushing from the Android app to the OS, but the writes to the disk may be buffered and delayed by the OS. To deal with this, I don't use FileWriter, but FileOutputStream (sometimes wrapped in an OutputStreamWriter), so you can call getFd().sync() on the FileOutputStream after calling flush() and before calling close().
Fourth, you won't be able to see this file and its contents in your desktop OS file manager until it gets indexed by MediaStore. Either test your results using other means (e.g., adb pull), or use MediaScannerConnection and its scanFile() method to tell the MediaStore to index your newly-created file.
The following code demonstrates everything but the MediaScannerConnection bit:
private static class SaveThread extends Thread {
private final String text;
private final File fileToEdit;
SaveThread(String text, File fileToEdit) {
this.text=text;
this.fileToEdit=fileToEdit;
}
#Override
public void run() {
try {
fileToEdit.getParentFile().mkdirs();
FileOutputStream fos=new FileOutputStream(fileToEdit);
Writer w=new BufferedWriter(new OutputStreamWriter(fos));
try {
w.write(text);
w.flush();
fos.getFD().sync();
}
finally {
w.close();
}
}
catch (IOException e) {
Log.e(getClass().getSimpleName(), "Exception writing file", e);
}
}
}
(from this sample project)

How to open Context.MODE_PRIVATE files with external apps

I have a string (called comments) that contains some text that I want to display using an external app. I initially create the file like so:
String end = "rtf";
FileOutputStream outputStream;
try {
outputStream = openFileOutput("document." + end, Context.MODE_PRIVATE);
outputStream.write(comments.getBytes());
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
However I am unable to open the file with an external application when I try the following:
String type = "text/rtf";
Intent intent = new Intent (Intent.ACTION_VIEW);
File file = new File(getFilesDir() + "/document." + end);
Uri fileUri = Uri.fromFile(file);
intent.setDataAndType(fileUri,type);
startActivityForResult(intent, Intent.FLAG_GRANT_READ_URI_PERMISSION);
The message that I receive when I open try to the document with the external app is:
"open failed: EACCESS (Permission denied)."
Please advise. Thanks.
However I am unable to open the file with an external application when I try the following:
Correct. Intent.FLAG_GRANT_READ_URI_PERMISSION is for use with a ContentProvider, not for bare file:// Uri values, such as you are using. Use FileProvider to add such a ContentProvider to your app. See also the "Sharing Files" training module and this sample app.
Bear in mind that there's a good chance that your next problem will be an ActivityNotFoundException, as relatively few Android devices will have an app that will support the text/rtf MIME type.

Proper way to share an Image (using Intents)

I create images in my app and want to share these social networks (facebook), mail apps (gmail), and other apps that can "receive" images.
The origin of the problem (I think) is that I don't want to use the external storage as a base for my images. I want to either use my data folder or my cache folder since neither of these require any permission to access.
The code which I use to write my image to file (and I specify the MODE_WORLD_READABLE so that other apps can read them):
FileOutputStream fos = null;
try {
fos = context.openFileOutput("image.jpg", Context.MODE_WORLD_READABLE);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
} finally {
if (fos != null)
fos.close();
}
And this is the code where I share the image:
File internalFile = context.getFileStreamPath("image.jpg");
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(internalFile));
intent.setType("image/jpeg");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(Intent.createChooser(intent, "share"));
This solution is very easy and works fine for apps like facebook but not for example gmail which failes with:
file:// attachment paths must point to file:///mnt/sdcard
There are a number of "hacks" (see below) to get it to work with gmail but I leaves me asking myself if there is an even better way to share images that works without hacks, something I overlooked. So, to the questions:
What is the best way to share images? (external storage?)
Is there any more apps that (mis-)behave just like gmail? (I have seen some trouble with google+)
If there is no other way: Can I write special intents for sharing to specific apps. I have a default way of sharing and override it when the user selects an app on my watch list?
Hacks
Using a path-hack by simply pointing the Uri to:
file:///mnt/sdcard/../../my/package/name/...
This solution doesn't feel right.
Using a ContentProvider as described here. But quoted from the link:
Warning: the method described in the post works well for Gmail, but apparently has some issues with other ACTION_SEND handlers (e.g. the MMS composer).
(Issue: It crashes the MMS composer)
Did you try ParecelableFileDescriptor?
http://developer.android.com/reference/android/os/ParcelFileDescriptor.html
Create with
static ParcelFileDescriptor open(File file, int mode, Handler handler, ParcelFileDescriptor.OnCloseListener listener)
Create a new ParcelFileDescriptor accessing a given file.
static ParcelFileDescriptor open(File file, int mode)
Create a new ParcelFileDescriptor accessing a given file.
Receiver side like this:
Returning an Input Stream from Parcel File Descriptor using Androids DownloadManager
You should to make 3 steps.
Take picture.
public Bitmap takeScreenshot() {
View rootView = findViewById(android.R.id.content).getRootView();
rootView.setDrawingCacheEnabled(true);
return rootView.getDrawingCache();
}
Save picture.
public String saveBitmap(Bitmap bitmap) {
File imagePath = new File(Environment.getExternalStorageDirectory() + “/screenshot.png”);
FileOutputStream fos;
try {
fos = new FileOutputStream(imagePath);
bitmap.compress(CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
Log.e(“GREC”, e.getMessage(), e);
} catch (IOException e) {
Log.e(“GREC”, e.getMessage(), e);
}
return imagePath.getAbsolutePath();
}
Share to social network.

Public URI on Android where I can store a VCARD so that Android vcard import activity can see it? (NO SDCARD)

I need to import a vcard in android on a phone that has no SD Card, no external storage. I have access to the vcard file as a string/stream (whichever). I want android to handle the import by calling the following:
Intent i = new Intent();
i.setAction(android.content.Intent.ACTION_VIEW);
i.putExtra("account_name", accountName);
i.putExtra("account_type", accountType);
i.setDataAndType(Uri.parse("file://" + importFile.getAbsolutePath()), "text/x-vcard");
startActivity(i);
I am getting a permission error because I cannot find a place I can put the file that the default android activity can have read permission. I tried saving a file like this:
FileOutputStream openFileOutput = ctx.openFileOutput("contacts.vcf", Context.MODE_WORLD_WRITEABLE);
try {
openFileOutput.write(fileAsString.getBytes());
openFileOutput.flush();
} catch (Exception e) {
e.printStackTrace();
} finally{
try{openFileOutput.close();}catch (Exception e) {}
}
And then passing it the URI by using:
File fileStreamPath = ctx.getFileStreamPath("contacts.vcf");
Uri uri = Uri.parse("file://" + fileStreamPath.getAbsolutePath()), "text/x-vcard");
But i get the permission error.
I looked at the activity, and the only thing it accepts is a URI, it doesn't take a stream, or a string so I cannot send it the vcard file in either of these ways, it has to be a URI (which is complicating this problem greatly...).
So my question:
How can I get this vcard which I currently have as a string, into a public URI so that the android activity that imports vcards can have access to it?
Thanks very much for reading.

Categories

Resources