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?
Related
I have a gallery which is maintained through app and didn't want to give images a jpg extension since I didn't want images to display in phone gallery so users cannot accidentally delete them. Filenames are something like "gall.22", "gall.381", etc. In fact, there are jpeg files.
When I share one or multiple images, they are shared with their filename which is not ending with .jpg and therefore cannot be opened regularly if shared to email, Viber, etc.
Is there a way to share files with a custom name, not the original one (to add just .jpg at the end of filename) and to avoid copying file to another file with wanted name prior sharing?
I didn't want images to display in phone gallery
I think you can try another way instead of rename extension, you can create the folder to store images and create file .nomedia inside this folder. MediaScaner will ignore this folder when scan and you can share image in normal way.
to avoid copying file to another file with wanted name prior sharing
When you share a file by Uri to another app can handle directly by Uri or copy, it depends on another app handle
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.
Lets say I have an App that allows me to rate mugs. Therefore, I enter some rating criterias, take a Picture of the mug and then save it to a DataBase, to look at it later.
At the moment, I save the picture on a path obtained by
getExternalFilesDir(Environment.DIRECTORY_PICTURES)
and save the path to my SQLite database to look at it later.
This works fine, however, as I rate a hole bunch of mugs all day and change my phone quiet often, I want to be able to backup my App and its data by common Android backup solutions. This works fine with the SQLite Database that holds the data and is stored in App-Context.
But since the database just holds a path to the taken picture (as returned by getExternalFilesDir), the picture is not backed-up. Where do I have to save the picure, to ensure that any common Android backup software will also grap the pictures?
Is it possible to ensure that the path stored in the database is the same, after I put the backup on a new phone? Since it may be possible that the App is located somewhere else on the new phone, absolute paths are not a good idea here... Is it possible to save the picture relative to the App and just save the relative path?
As suggested by greenapps, a possible solution is to save just the relative path of the image in the database. Instead of using
getExternalFilesDir(Environment.DIRECTORY_PICTURES)
I now use
getFilesDir()
to get the app intern folder and save images there. When I want to load the picture I build the path with the same method to get the app dir and the relative path saved in the database.
I wanted to know the best approach to passing information from an app to another app on the same devices in android.
For example:
I open google apps and I share a document with my App A.
Google App generated an intent and sends a content URI. From my
understanding, the content uri contains information about the file
(filename, file size, mimetype) and the ability to extract the
content which is located in the cache of the google app on the
device.
When App A opens, it reads the content URI. Ideally, it
should be able to extract the information from the content uri and
then render the image. What this means is that App A will display the image shared. In this example, google app shares a docement, and App A wants to open and display the document within it's own app.
The confusing part
From searching the web, it seems that some people actually try to
extract the file path from the content URI. This requires that you
have permission to access another app's cache or storage space
within the device. Let's say this is possible. It also makes some
assumptions that it's possible to extract the file path.
After reading some articles:
https://commonsware.com/blog/2016/03/14/psa-file-scheme-ban-n-developer-preview.html
https://commonsware.com/blog/2014/07/04/uri-not-necessarily-file.html
https://commonsware.com/blog/2016/03/15/how-consume-content-uri.html
it seems that, ideally you should never assume that you can extract the file path and that google has made some updates that makes this not possible.
Work around:
Eventhough i'm not able to extract the file path from the
contentUri, I'm able to read the bytes of what the contentUri is
pointing to. So I could save it to a file that is relevant to the
local cache of App A and pass that path along to get render or pass
the bytes back. This refers to App A displaying the content. That is passing the path or bytes and let's make the assumption that it knows how to display it given that information.
Question:
The work around does not seem ideal because technically you are
save the file again on the device. There are two locations with the
same content ( google app storage and App A's storage). You also
have to manage when to delete the App A's file that you created.
This doesn't really seem ideal and was wondering what the best
approach would be? Or is this the expected flow?
Also I don't know
if it's ideal to pass the bytes back up vs. just a file path.
Update
To be more specific, the app i'm creating is a hybrid where i'm using cordova plugin to interact with a web app. The web app has methods to process or display the shared document based on file path. So ideally I want to keep it consistent with just reading the file path so that the other platforms that the web app supports does not break.
Any advice appreciated,
D
Eventhough i'm not able to extract the file path from the contentUri, I'm able to read the bytes of what the contentUri is pointing to.
Correct. This is not significantly different than how you use an HTTPS URL, where you also do not have direct filesystem access to the content (in that case, resident on a different server).
So I could save it to a file that is relevant to the local cache of App A and pass that path along to get render or pass the bytes back.
Or, just consume the bytes. Again, drawing an analogy to an HTTPS URL, there is no requirement to save those bytes to disk to use them.
The work around does not seem ideal because technically you are save the file again on the device. There are two locations with the same content ( google app storage and App A's storage). You also have to manage when to delete the App A's file that you created.
Then do not save the file again on the device, and simply use the stream of bytes. Again, this is not significantly different than using an HTTPS URL.
This doesn't really seem ideal and was wondering what the best approach would be?
Do not write the bytes to disk. Just use them.
So ideally I want to keep it consistent with just reading the file path so that the other platforms that the web app supports does not break.
Your choices are:
Improve the Web app code, such that a local file path is one possible source of the data, or
Suffer the problems with making copies of that data
After all, bear in mind that the Uri you are given via ACTION_SEND does not have to be a content Uri. It could very easily be an http or https Uri.
What i want is that i have made a picture chooser, the image chosen i then want to save in my own object.
Like i have a class called personalFile, this file then includes all details, name, birthdate etc.. I have made it so i can update this personal file and save them to the external storage getExternalFilesDir() using an ObjectOutPutStream.
Now i have taken a picture of the person, and have browsed my way to the picture and gotten the uri path to the picture, now how do I save this file together with the rest of the information in my class?
I have tried looking but havnt found anyone doing this. The reason is also i'd like to be able to send the personal record to another phone using mms, email or alike and be able to open it there with the picture and all.
One way of doing it (albeit not the most efficient) would be to have a byte[] image; member variable in your class and read the content of your file into that byte array. You are quite likely to experience issues though if the image size is large. The other alternative would be to just copy the image file to your external storage and inside your class simply save the file name of the file. Either way, I'm not sure of the merit of saving images as part of your persistent app data (unless the images are really small).