In my application I want to give users ability to open, edit and save image from Android photo library.
Opening is easy, as well as saving as a new file. The problem is with overwriting images (opened from the photo library).
When saving changes I simply overwrite files, knowing the path of the original file.
If user saves a brand new file (not opened from library, but created in the app), the file is saved in
Android.OS.Environment.DirectoryPictures folder.
(e.g. /storage/emulated/0/Pictures/img_2019921_212552.jpg)
I generate random file name, append to the directory path, save, and the file is there.
Knowing this path I can later update file contents by simply overwriting the file.
If however, I open a file from the photo library (including also these files that I had just created with the app), the path that is visible
to me is a kind of weird (e.g. /document/image:87799). I am able to get file contents using this pseudo-code:
Android.Net.Uri uri = intent.Data;
Stream stream = ContentResolver.OpenInputStream(uri);
Upon saving however, the only thing with regard to the photo location that I have is this strange path (e.g. /document/image:87799)
which of course cannot be used to open write stream to the file.
What I need is:
a way to resolve this strange path into a kind of physical one, which will work with streams (to overwrite file).
Or maybe I'm doing it the wrong way...? Updating image from photo library seems to be a standard task, which may have a dedicated
APIs / good practices ?
After my verification, as suggested by CommonsWare:
The following code can be used to save changed image to photo library.
var uri = Android.Net.Uri.Parse(currentPhotoURL);
using (var stream = contentResolver.OpenOutputStream(uri))
{
await stream.WriteAsync(data, 0, data.Length);
}
What is important, no file system APIs are used here.
Now, as overwriting existing file is solved (knowing the URI of the original image), how can I write a brand new image (byte[] array e.g. with encoded jpeg), without using fileSystem APIs ? (this is what I currently do for 'save as new file' operation). Eventually I will need to have URI to the image (starting with content://) - so as to be able to overwrite that copy, if used decides to overwrite it after a while.
Related
Premise: I am using WhatsApp on Android.
Why I am asking this
I am trying to create a script that, when configured with proper arguments, would restore accidentally deleted pictures in a WhatsApp chat, if you have a copy of those files somewhere else (with potentially different file names) and you pass them to the script.
What happens when you delete an image
Normally, if you accidentally delete the image files, WhatsApp still shows you a thumbnail, but if you click on it the following pop-up appears.
Therefore, I guess it looks for a filename in the storage and it tries to load that file.
I verified this
I tested this by manually replacing an image in the "WhatsApp Images" folder with another image, and the new image shows up in the corresponding chat as expected, so this is right.
But when I did that test, I knew the file name because I manually watched the file previews using a file manager and I identified the right file, so that was easy.
So the problem is
What about deleted files? how can I try to guess the corresponding file names in order to place the file copy back where it is supposed to be?
What I already know
First of all, I know the file names have this format:
IMG-yyyymmdd-WAxxxx.jpg
Where yyyymmmmdd is a date and xxxx is a sort of sequence number.
I also know that the mapping from thumbnails to actual files is stored in the msgstore.db database file, but it can only be accessed if you have root privileges. Do you think is there any other way to predict what the file name can be without accessing that DB?
My app needs to read an image file and store the location in a settings database.
I found how to grab an image using the ACTION_GET_CONTENT intent that opens the media selector. It returns a Uri that looks like content://media/external/images/media/78
I understand this allows saving every kind of data source, not only files. I still have two questions about that.
How is this resolved to an actual file in a folder? Is a database maintained that builds a relation between the number and the very different filename?
If the app wants a local file as a background file for this scenario, should I save this Uri in the preferences or better the real filepath?
Is it somehow possible to get a File object from an DocumentFile? With some small trick I can get the real path of the file, but the File class is not allowed to read/write there...
I need to access media files from USB OTG or secondary storage and I need following functions:
get exif data from images => I really need that for
getting the location of the image
getting the real creation date of the image
actually, for displaying purpose, I need all exif data
ability to rotate images (this would be possible by creating a temp image, delete the old one and rename the temp image)
Any idea on how to achieve that?
Is it somehow possible to get a File object from an DocumentFile?
No.
With some small trick I can get the real path of the file
Not reliably. After all, the Storage Access Framework does not require there to be an actual file. Various DocumentProvider implementations work off of cloud services, where the data is not stored locally on the device until needed. Beyond that, whatever approach that you are using is dependent upon internal implementation that may vary by device, let alone Android OS version. And, to top it off, you still cannot necessarily access the data even if you derive a path, as you may not have filesystem access to that location (e.g., files held on internal storage by the DocumentProvider).
get exif data from images
Use the stream, along with EXIF code that can work with streams, such as this one or this one, found by searching the Internet for android exif stream.
ability to rotate images (this would be possible by creating a temp image, delete the old one and rename the temp image)
You don't have a choice to make a local copy, rotate the image, and then write the image back to the original DocumentFile using an OutputStream. Whether that "local copy" is only in RAM, or needs to be an actual file on disk, depends a bit on how you were planning on rotating it.
Currently I am able to download a file off the internet and store on the SD card, then use the file from there. However that makes the file (with proprietary data) available to be seen. I would prefer to use the file from somewhere like raw or assets folder.
I will skip the downloading code, but my code to use the file is this
File myFile = new File (android.os.Environment.getExternalStorageDirectory() + "/folder/filename.xml");
Intent myIntent = new Intent(Intent.ACTION_VIEW);
myIntent.setData(Uri.fromFile(myFile));
Android opens the file with the default application and all is good.
I have found similar Q/A's that revolve around using code like
Uri.parse("android.resource://com.projectname.testing/raw/filename");
and
InputStream ins = getResources().openRawResource(R.raw.filename);
but I can't work out how to get either of those two back into a 'file' format to be used with my .setData code
I would like to solve my problem by simply accessing the file as a file. However since it is being used by an external application I have read I might need to make a temporary copy of the file with mode_world_readable then delete it after the application closes. This sounds like a lot of extra work, especially since my code above does work for a file stored on the SD card.
Does anyone have any ideas?
Thanks
I would prefer to use the file from somewhere like raw or assets folder.
Note that these too can be "seen".
but I can't work out how to get either of those two back into a 'file' format to be used with my .setData code
setData() does not take a File. It takes a Uri. Use Uri.parse() to parse other types of Uri values -- you already have this code shown above.
However since it is being used by an external application I have read I might need to make a temporary copy of the file with mode_world_readable then delete it after the application closes.
It definitely will need to be world-readable. Also, not all apps support all schemes, so apps that support file:// or http:// might not support android.resource://.
I have written a program that uses the Intent for the image capture to get a photo using the application in the phone.
Using MediaStore.EXTRA_OUTPUT, I get a URI to the image, which converted to a path results in something like "/external/images/media/NN" where NN is the number of the photo.
Now, in my program, after I read and manipulated the image, I want to delete that image.
How should I do that?
(File image = new File(path); image.delete(); // returns false, so doesn't work)
Thanks for the answer.
I resolved reading this answer, Problems saving a photo to a file
For me it works on Hero, even if in the comment in that code snipped says that Hero behave differently.
Now I get the image in "/sdcard/image.tmp", and I can delete it.
I think this is the best solution, cause I think it's a trouble trying to get the camera app to write in my app directory.
Thanks again.
Is it possible that you've failed to request the WRITE_EXTERNAL_STORAGE permission in your app? This would cause deletions to fail. (The camera would be able to write regardless, since it is a separate app with its own permissions.)
It's also possible (but not likely) that the media folder is only writable by the camera app, in which case you'd want to specify a different desired destination in the EXTRA_OUTPUT extra of the intent which invokes the camera so that the file will be written into your app's directory. In fact, you probably want to do that anyway to avoid cluttering up the global space with private resources, even if you are going to delete them immediately.