I want to create a file from uri in the ActivityResultCallback in android 11. I use uri.getPath() to convert the incoming uri to a file. but, it won't work in android 11. here is my codes:
private void launchGallery (){
Intent intent = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
launcherGallery.launch(intent);
}
ActivityResultLauncher<Intent> launcherGallery = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK) {
Intent data = result.getData();
if (data != null) {
Uri imageUri = data.getData();
// ---> getPath() won't work in android 11 <---
File file = new File(imageUri.getPath());
// I don't want to display the image in an ImageView.
// I need a file object to pass it to this method to encrypt it
encryptImage(file);
}
}
});
so, how can I create a file from uri in android 11?
simply: you can't. if Uri would be always a File then there would be no purpose of both class existance. Uri is a "pointer" to a file, which can be read as InputStream
InputStream inputStream = getContentResolver().openInputStream(uri);
you have to read all these bytes and if you REALLY need a File then store this data in your apps Scoped Storage. if you are shure that this is an image (its declared in calling Intent) then you may try to convert this data to Bitmap.
edit: I see you've added encryptImage(..) method call, so for shure you can read Bitmap "straight from" Uri instead of a File without taking users storage space, even for a while
check out such case: note that when you run a file picker launchGallery() then inside of it you can switch folders. there is a possibility to pick file from e.g. Google Drive, or if you have installed, other web-storages (OneDrive, Dropbox etc.). you will get an Uri as a result, but this isn't a real File on local storage. so you can stream it (as through web/network) for reading entirelly, it isn't ready and available at the moment of result callback call (just after picking and returning to your app)
btw. such possibility isn't limited to Android 11 and above, it was there way earlier. just handle Uri as "pointer", do not assume it is pointing on (local) File. in some cases even if user pick local file you will get an Uri pointing on it through some ContentResolver (so use own for read data), thus this won't be a file path (won't be starting with file://)
Related
In my app, I did code for selecting a directory with persistence permission using android ACTION_OPEN_DOCUMENT_TREE. I did everything successfully, but the problem is I can not create a file inside the sub-directory and cannot get a list of files from the sub-directory. It gives me errors like Permission Denial: writing com.android.externalstorage.ExternalStorageProvider uri
but according to android official doc, it says Your app can then access any file in the selected directory and any of its sub-directories.
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
activity.startActivityForResult(intent, PERMISSION_CODE);
in onActivityResult
if (requestCode == PERMISSION_CODE) {
if (resultData != null) {
Uri treeUri = resultData.getData();
final int takeFlags = resultData.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
context.getContentResolver().takePersistableUriPermission(treeUri, takeFlags);
if(DocumentFile.fromTreeUri(context, treeUri).findFile("backup") == null){
Uri backupDirUri = DocumentFile.fromTreeUri(context, treeUri)
.createDirectory("backup").getUri();
//this statement gives me error
DocumentFile.fromTreeUri(context, backupDirUri)
.createFile("text/plain", "34234234.txt")
.getUri();
}
}
}
Suppose an app user select a directory inside SDcard named with MyFolder then I have created a directory backup inside MyFolder but I can not create a file inside the backup directory using the backup directory URI.
For some reason using getUri() on a DocumentFile instance and then attempt to create another DocumentFile from that Uri fails.
Solution: don't attempt to operate on a Uri. Work with the DocumentFile instance.
The first call to fromTreeUri must be using an Uri or Uri from String. But from that point create new files by using the returned DocumentFile.
For example, you can try this:
if (requestCode == PERMISSION_CODE) {
if (resultData != null) {
Uri treeUri = resultData.getData();
final int takeFlags = resultData.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
context.getContentResolver().takePersistableUriPermission(treeUri, takeFlags);
DocumentFile root = DocumentFile.fromTreeUri(context, treeUri);
if (root.findFile("backup") == null) {
DocumentFile backupDirUri = root.createDirectory("backup");
// this statement should not give error now
DocumentFile f = backupDirUri.createFile("text/plain", "34234234.txt");
// Write to your just created file.
// openOutputStream with f.getUri() will work at this point.
// For this example, plain OutputStream to write bytes,
// wrap it with writers or something more functional.
OutputStream os = context.getContentResolver().openOutputStream(f.getUri());
os.write("It works!".getBytes(Charset.forName("US-ASCII"));
os.flush();
os.close();
}
}
}
It's not only that going back and forward from DocumetFile to Uri then to DocumentFile again is very inefficient if done in the same function call, but that it always fails.
Why it fails?
Well... sorry, I don't know. I just know that in my test devices it fails. I don't know why.
You can safely going from Uri to DocumentFile in future iterations, or with files that already exists in the file system (I mean, not just created during that same function call).
You cannot go from Uri to DocumentFile if you just created a file during that same function call. But you can safely operate on the returned DocumentFile of the just created file. That's why you must remember the returned DocumentFile, not its Uri, if you plan to immediately operate on that just created file.
If you just want to operate on the file at a later time, then you can save the Uri. And you probably want to remember it, because I'm not sure of what happens to a DocumentFile instance once the app is suspended then resumed. In that case, Uri may be safer. Also the string representation of a Uri is what you can save to preferences or any kind of data base/custom file.
But again, if you want to immediately do something with the created file, don't try to get its Uri and then create a DocumentFile. Just use the DocumentFile you already got.
To me, it looks like DocumentFile.createDirectory and DocumentFile.createFile returns before the device file system is updated. This is a guess. I still has to hear the true explanation.
That would explain why you can operate with the returned DocumentFile instance, of a just created child file/directory, but if you save the Uri and try to call any DocumentFile function that accepts an Uri, with the Uri of a just created file/directory, then it fails.
Another possibility is that you cannot create a second DocumentFile while another refers to the same uri. Remember that you may not have references to it, but it exists until garbage collection.
I've been stuck with this for almost a week. Triend almost any answer/tutorial I found, but no luck yet.
I have an app that uses the camera. After the user takes a pic, and select the check/OK button in the camera, the camera returns, among other things, the Uri where the image was stored. It cames in the form of: content://media/external/images/media/122 (this is an actual pic in the phone), if I ask for the Uri.path I'll get in this case: /external/images/media/123
Now, with the Uri object, I can assign it to an imageView like imageView.setImageURI() and it works, so I'm pretty confident the images are stored somewhere.
Now, next thing my app should do, is take all the pictures taken, and send them to an API.
My problem is, when I try to read the Uri with any method, it gives me a FileNotFound Exception, no matter if I use the Uri or the path.
So I really don't know what to do. What am I missing? Do I have to explicitly save the image in a working directory? If so, how is it done? If not, how do I get the full path name including the filename? When I look at the DCIM directoy in the phone, I don't find the pics.
Thanks in advance!
EDIT
Code to take the pic:
btnTomarFoto.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(android.Manifest.permission.CAMERA) ==
PackageManager.PERMISSION_DENIED || ContextCompat.checkSelfPermission(
this,
android.Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_DENIED
) {
// El permiso no fue concedido
val permission = arrayOf(android.Manifest.permission.CAMERA,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
requestPermissions(permission, PERMISSION_CODE)
} else {
// El permiso ya estaba concedido
openCamera()
}
} else {
// El SO es menor que Marshmallow
}
}
}
Code to open the camera:
private fun openCamera() {
val values = ContentValues()
values.put(MediaStore.Images.Media.TITLE, "New Picture")
values.put(MediaStore.Images.Media.DESCRIPTION, "From the camera")
image_uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, image_uri)
startActivityForResult(cameraIntent, IMAGE_CAPTURE_CODE)
}
Code to store the Uri into the database:
private fun guardarFoto() {
var foto = FotografiaModel(actividad_id!!, image_uri.toString(), txtObservacion.text.toString())
Log.w(tag, "URI: $image_uri PATH: " + image_uri!!.path + " Encoded Path: " + image_uri!!.encodedPath)
fotografiaDBHelper.insertFoto(foto)
visitaDBHelper.actualizaEstado(actividad_id!!, "INICIADO")
}
There are some more things the code does, but this is the main thing. To show the received Uri into an Imageview I use:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
imageView.setImageURI(image_uri)
Log.v(tag, "Image URI: $image_uri")
}
}
My problem is, when I try to read the Uri with any method, it gives me a FileNotFound Exception, no matter if I use the Uri or the path.
That is because it is not a file. It is a Uri.
For example, https://stackoverflow.com/questions/56839478/kotlin-how-to-read-a-uri-in-android is a Uri. Your code assumes that the path /questions/56839478/kotlin-how-to-read-a-uri-in-android is the only thing that matters in a Uri. By your argument, every single computer on the planet (plus those in orbit) have a file located at /questions/56839478/kotlin-how-to-read-a-uri-in-android. That is not how a Uri works. You need to use the entire Uri to determine how to use it. In this case, https as a scheme means that you use HTTPS as a protocol to talk to the designated server (stackoverflow.com) to retrieve the content.
Similarly, the content scheme in your Uri from insert() indicates that you use ContentResolver to work with it. In particular, you can use openInputStream() to get an InputStream on the content identified by the Uri, just as you might use HttpUrlConnection or OkHttp to get an InputStream on an https Uri.
In particular, since you delegated data storage to some other app (the MediaStore), where the image is stored is up to somebody else, and it is not necessarily on the filesystem in a place where you can access it. That is particularly true on Android Q and higher, where you have limited access to arbitrary locations on the device via filesystem APIs.
Now, next thing my app should do, is take all the pictures taken, and send them to an API.
I am going to guess that "send them to an API" means "upload them to a server" (versus "call an API exposed by a library on the device" or "call a method in the Android SDK"). In that case, you have some code for talking to that server. It might be general-purpose code (e.g., OkHttp, Retrofit) or API-specific code (e.g., Facebook SDK).
Regardless, you will need to see what that code supports for your image content:
If it supports a Uri, try using your Uri
If it supports InputStream (or a Reader of some type), use openInputStream() on a ContentResolver
If it supports a FileDescriptor or AssetFileDescriptor, use openFileDescriptor() on a ContentResolver
If it only supports File, instead of using contentResolver.insert() to get the Uri to send to the camera app, use FileProvider, so you can have the images saved in a file that you control (e.g., in getCacheDir())
Etc.
I am having a problem with selecting image file from external storage using file picker in Android. This question is the consequence of this question - No such file or diectory error in image file upload using Retrofit in Android. What my problem is opening and reading file from external storage on activity result. I want to convert result URI into File.
I read a pdf file from download folder on activity result
Uri bookUri = data.getData();
if(bookUri!=null)
{
String filePath = bookUri.toString();//bookUri.toString()
String mime = app.getMimeType(filePath);
if(mime!=null && !mime.isEmpty() && (mime.toLowerCase()=="application/pdf" || mime.toLowerCase()=="application/txt" || mime.toLowerCase()=="application/text"))
{
bookFile = new File(bookUri.getPath());
ivBookFile.setImageResource(R.drawable.book_selected);
}
else{
Toast.makeText(getBaseContext(),"Unable to process file you have chosen.",Toast.LENGTH_SHORT).show();
}
}
As you can see I used new File(bookUri.getPath()); to convert into File. The above code works well. It is working. The problem is now I am trying to open an image file in DCIM/Camera folder on activity result.
This is the code I used
Uri selectedImageUri = data.getData();
if(selectedImageUri!=null)
{
try{
bmpCoverImage = MediaStore.Images.Media.getBitmap(getContentResolver(), selectedImageUri);
imageFile = new File(selectedImageUri.getPath());
if(bmpCoverImage!=null)
{
ivCoverImage.setImageBitmap(bmpCoverImage);
}
}
catch (IOException e)
{
Toast.makeText(getBaseContext(),"An error occurred with the file selected",Toast.LENGTH_SHORT).show();
}
}
As you can see I used new File(selectedImageUri.getPath()); like I did in reading pdf file. This time the code is not working. When I do operation with the file like in previous question, it gives me error.
I used this way also
imageFile = new File(Environment.getExternalStorageDirectory(),selectedImageUri.getPath());
I got the same error. How can I open the image file correctly from external storage? How can I convert the chosen file URI from external storage into File?
I am having a problem with selecting image file from external storage using file picker in Android
If you are referring to the code that you are using in this question, you are not "using file picker". You are using ACTION_GET_CONTENT, which has never been a "file picker", nor will it ever be a "file picker".
I want to convert result URI into File.
Usually, that is not necessary. But, if that is what you want to do:
use ContentResolver and openInputStream() to get an InputStream on the content represented by the Uri
create a FileOutputStream on your desired file
use Java I/O to copy the bytes from the InputStream into the FileOutputStream
The above code works well. It is working.
It works for the small number of devices that you tested, for the specific activities that the user chose to handle the ACTION_GET_CONTENT request. It will not work on most Android devices, and it will not work in most circumstances. The only time that code will work is if the Uri has a file scheme. Most of the time, it will not. Instead, it will have a content scheme, representing content supplied by a ContentProvider.
Please how can I open the image file correctly from external storage?
If you wish to continue using ACTION_GET_CONTENT, please understand that this has nothing to do with external storage specifically. You are not getting a file, on external storage or elsewhere. You are getting a Uri. This is akin to a URL, such as the URL for this Web page. Just as a URL does not necessarily point to a file on your hard drive, a Uri does not necessarily point to a file on the filesystem. Use a ContentResolver and DocumentFile to work with the Uri and the content that it identifies.
If you want to always get files on external storage (and nowhere else), then use an actual file picker library.
I am using JAudioTagger library for reading and writing tags for an audio file. I am able to read the tags but unable to write them.
I am retrieving audio file path like this :
private String getSongPath(long songId) {
String path = null;
ContentResolver contentResolver = getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Audio.Media.DATA};
String selection = MediaStore.Audio.Media._ID + " == ?";
String[] selectionArgs = {String.valueOf(songId)};
Cursor cursor = contentResolver.query(uri, projection, selection, selectionArgs, null);
if (cursor != null) {
int pathCol = cursor.getColumnIndexOrThrow(projection[0]);
cursor.moveToFirst();
path = cursor.getString(pathCol);
cursor.close();
}
return path;
}
Then to write tags using JAudioTagger :
File songFile = new File(path); // path looks like /storage/3932-3434/Music/xyz.mp3
AudioFile audiofile = = AudioFileIO.read(songFile);
Tag tag = = audiofile.getTag();
tag.setField(FieldKey.TITLE, title);
// some more setField calls for different feilds
audiofile.commit();
The commit() method is giving following Exception :
org.jaudiotagger.audio.exceptions.CannotWriteException:
java.io.IOException: Cannot make changes to file xyz.mp3 at
org.jaudiotagger.audio.mp3.MP3File.commit(MP3File.java:799) at
com.techapps.musicplayerplus.MainActivity$17.onClick(MainActivity.java:2125)
at
android.support.v7.app.AlertController$ButtonHandler.handleMessage(AlertController.java:157)
at android.os.Handler.dispatchMessage(Handler.java:102) at
android.os.Looper.loop(Looper.java:148) at
android.app.ActivityThread.main(ActivityThread.java:5417) 06-18
10:59:48.134 8802-8802/com.techapps.musicplayerplus W/System.err:
at java.lang.reflect.Method.invoke(Native Method) at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused
by: java.io.IOException: Cannot make changes to file Saibo.mp3 at
org.jaudiotagger.audio.mp3.MP3File.precheck(MP3File.java:824) at
org.jaudiotagger.audio.mp3.MP3File.save(MP3File.java:850) at
org.jaudiotagger.audio.mp3.MP3File.save(MP3File.java:783) at
org.jaudiotagger.audio.mp3.MP3File.commit(MP3File.java:795)
I am running this code on Android 6 while my app is targeted at SDK 22. I have also mentioned following permission in manifest.
android.permission.WRITE_EXTERNAL_STORAGE
Still I am unable to write to SD card. Please help me. Thanks in advance.
You have to use Storage Access Framework (SAF) to access SD Card from API 19 (Kitkat) onward.
First we need to ask user to provide a URI of the folder we want to access. If we want access to entire SD card, user needs to provide URI of SD card's root folder.
For example, when user hits Edit button, we have to first show hint dialog box, asking user to select required directory in SD Card which we want to access. You can display following image in hint dialog box to ask user to select root directory of SD Card :
When user dismisses hint dialog box, you need to trigger Storage Access Framework :
private void triggerStorageAccessFramework() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_STORAGE_ACCESS);
}
public final void onActivityResult(final int requestCode, final int resultCode, final Intent resultData) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_STORAGE_ACCESS) {
Uri treeUri = null;
// Get Uri from Storage Access Framework.
treeUri = resultData.getData();
pickedDir= DocumentFile.fromTreeUri(this, treeUri);
if (!isSDCardRootDirectoryUri(treeUri)) {
Toast.makeText(this, "Wrong directory selected. Please select SD Card root directory.", Toast.LENGTH_LONG).show();
createSDCardHintDialog().show();
return;
}
// Persist URI in shared preference so that you can use it later.
SharedPreferences sharedPreferences = getSharedPreferences(App.PREFERENCE_FILENAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(App.SDCARD_URI_KEY, treeUri.toString());
editor.apply();
// Persist access permissions, so you dont have to ask again
final int takeFlags = resultData.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
private boolean isSDCardRootDirectoryUri(Uri treeUri) {
String uriString = treeUri.toString();
return uriString.endsWith("%3A");
}
Once you get Uri of user picked directory, you can perform write operation using SAF : (creadit : this answer )
public void writeFile(DocumentFile pickedDir) {
try {
DocumentFile file = pickedDir.createFile("image/jpeg", "try2.jpg");
OutputStream out = getContentResolver().openOutputStream(file.getUri());
try {
// write the image content
} finally {
out.close();
}
} catch (IOException e) {
throw new RuntimeException("Something went wrong : " + e.getMessage(), e);
}
}
It could be that you pointing to non existing file.
Check your path file by using Log.
Log.d("Activity", "path = " + path);
Android-M or API 23 introduced Runtime Permissions for reducing security flaws in android device.
To update your apps using Google Play services to handle Android 6.0 permissions, it’s good practice to manage the user’s expectations in setting permissions that the runtime may require. The following link will help you avoid potential issues.
https://developer.android.com/training/permissions/requesting.html
have you declared the permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ?
I saw that you already created an issue in the JAudioTagger GitHub repository which was advisable, but never got a universally working solution. My findings so far:
The answer mentioning SAF is correct, but it won't help you as SAF will provide a DocumentFile, not a File.
You might try to modify JAudioTagger to your needs, replacing File with DocumentFile, but the latter one has not all functions you will need.
Also InputStream and OutputStream will not help you, as JAudioTagger needs File and internally heavily uses RandomAccessFile which is not available either.
Google "forgot" to provide some getRandomAccessFileFromUri() which makes things even worse (Yes, there are hacks using Java reflection to work around this limitation...).
The "/proc/self/fd" method (How to handle SAF when I can only handle File or file-path?) will also not work immediately, as JAudioTagger needs copy and renaming functions that are not applicable to this kind of files. Particularly JAudioTagger will not find a suitable file name extension like ".m4a". Of course you could try to change JAudioTagger accordingly.
You might follow the advice to make a copy of the file to your personal storage, then apply JAudioTagger to it and finally copy it back to SD card, but:
If you want to use JAudioTagger to read from SD card, this will, as announced by Google, fail with Android 10. Starting with that version, you will not even have read access to the SD card via the File interface.
Further, the File interface gives you read access to SD cards with Android 9 and below, but not to other SAF devices, like USB OTG memory or SMB shares etc.
Of course you could also copy each file in order to read its metadata, but this will be awfully slow and is not suitable if you have more than a few files.
So my current advices are:
Try the "/proc/self/fd" method and modify JAudioTagger accordingly.
If the changes are too heavy, use the fd method for reading the tags and the copy method for writing.
BTW: I am currently modifying an older version of JAudioTagger for using both File and DocumentFile transparently, but the changes are tremendous, bear a high risk, need some help classes, and the work is unfinished, yet.
BTSW: The DocumentFile functions are painfully slow, compared to the File functions.
I am writing my first App. I am using KitKat and an Experia Z for testing.
My app will display photos that have been previously taken by the app.
I have been going round in ever decreasing circles, searching and trying, but never succeeding. I realise that I am possibly asking a duplicate question, but I have been unable to find it - and hence, the answer I need.
My starting point was from the Getting Started tutorials:
http://developer.android.com/training/camera/photobasics.html
I create the Intent using:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
I can get hold of the small image using:
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) data.getExtras().get("data");
BTW, I have just noticed that all my test photos taken this way are visible the 'Album' app, and have the traditional file path in a camera, such as '/storage/sdcard1/DCIM/100ANDRO/DSC_0120.jpg'
Is there a way to get hold of this file path (in the onActivityResult() method)?
When I provide a file system URI, using:
File storageDir =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File photoFile = new File(storageDir, "test.jpg");
mCurrentPhotoPath = "file:" + photoFile.getAbsolutePath();
The path for 'storageDir' displays as: "/storage/emulated/0/Pictures". Which looks like some kind of temporary or logical location.
I add an 'extra' to the intent:
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
However, when I try to get the photo, it is never found (and it isn't in the standard camera file system, shown above)
bmOptions.inPurgeable = true;
Bitmap imBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
The Bitmap imBitmap is null. Which suggest the the location in 'mCurrentPhotoPath' is not found.
AH! I don't know why 'mCurrentPhotoPath' is created with 'file:' as a prefix. I have removed it, and I now get my photo returned. (It was taken as portrait, but it is displayed across the ImageView, as if taken in landscape - no doubt that can handled).
However, I still don't know where it is. I have searched the phone and SD card using File Commander, and I can't find it. I would like to have the images stored in the same place as other photos, where they can be accessed by other apps.
Is there another step, whereby I ask for the image to be saved as a photo, so it gets the next file name in the 'DSC_nnnn.jpg' sequence? Or, as date-defined file path / name of my choice, on the same media defined for photos (internal or SD card)?
I would be really grateful for any help on this.
Regards, John
[edit 1]
Took phone to the beach - beach-weather days in the UK are a rare thing! This is my first Android phone - so I am in at the deep end.
In the relaxed conditions, I searched the phone using File Commander and, as stated in a reply, I found a 'Pictures' directory on the internal storage, and it contained the jpegs taken when I provided the location via the 'putExtra', and their location was as defined by Environment.DIRECTORY_PICTURES - so they are retrievable.
What I would prefer to happen is for the pictures to be placed wherever the Camera is set to store them - in my case this is on the SD card. This is what happens when I don't provide a target file location via 'putExtras' - which makes complete sense. I can get the thumbnail image using 'data.getExtras().get("data")'. Is there a way to get the file path for the actual image?
If that isn't possible, how can I get the location used by Camera on the SD card, in order to provide this as an Extra? All of the 'Environment.getXxxDir() methods look to return the internal storage.
[/edit 1]
The way to find the location of the photo just taken on the file system is:
No Extras to provide a file path and location. The image will be placed wherever defined in the Camera's settings (Internal or SD card)
Start the Activity to take the picture:
private static final int REQUEST_IMAGE_CAPTURE = 1;
...
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
Handle the result (the key is that data.getData() returns the URI of the photo's file):
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Uri imageUri = data.getData();
System.out.println("DisplayImageActivity#onActivityResult() - imageUri = "+imageUri);
String imagePath = getRealPathFromURI(imageUri);
System.out.println("DisplayImageActivity#onActivityResult() - image file path =
"+imagePath);
...
Get the actual path from the URI:
This code is from: Get file path of image on Android , and provided by Siddarth Lele (thanks Siddarth)
public String getRealPathFromURI(Uri uri) {
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
return cursor.getString(idx);
}
The values from the System.outs are:
imageUri = content://media/external/images/media/4777
imagePath = /storage/sdcard1/DCIM/100ANDRO/DSC_0143.JPG
I can now use the file path to get and show the photo:
Bitmap imBitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
imageView.setImageBitmap(imBitmap);
I now have a new challenge: the photo was taken in portrait mode, but the resultant image is shown in landscape mode across the top of the ImageView, instead of as taken. If I can't find or figure out how to rotate it, I will create another SO request.