I am writing a camera and gallery app, following the official Google documentation. The problem is that it mostly focuses on how to take a picture and how to save it in memory. I am saving them in the application external memory through getExternalFilesDir().
What I am now trying to do is simpy query that directory in which taken photos are saved, through MediaStore, in order to display all the pictures in there in a recycler view. But I can't seem to find anywhere an explanation on how to do the retrieval. There is something wrong with the Uri that I probably didn't understand well, and I don't know how to solve. Even in here https://developer.android.com/training/data-storage/files#PrivateFiles the article only talks about saving files in the various storage options, but not about retrieving them.
Here is the code of how I save the files:
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
currentPhotoPath = image.getAbsolutePath();
return image;
}
This is the function that actually sends the intent (and then onActivityResult handles the returned data):
private void dispatchTakePictureIntent() {
final String TAG = "dispatchTakePic: ";
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
Log.e(TAG, "Error while creating a file");
ex.printStackTrace();
}
// Continue only if the File was successfully created
// Authority has to be exactly like in manifest
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(
this,
"com.example.android.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}
This is the code where I'm trying to get the information about the photos taken:
private void populatePhotoArray() {
final String TAG = "popPhotoArray: ";
ContentResolver contentResolver = getContentResolver();
String photo_id, photo_title, photo_path = "";
final String[] PHOTOGRAPHS_PROJECTION = {
MediaStore.Images.Media._ID,
MediaStore.Images.Media.TITLE,
MediaStore.Images.Media.DATA};
File photographsDirectory = getExternalFilesDir(Environment.DIRECTORY_MOVIES);
Uri photographsUri = Uri.fromFile(photographsDirectory);
//Uri photographsUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
//Uri photographsUri = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
String sortOrder = MediaStore.Images.Media.DATE_ADDED + " DESC";
final Cursor cursor = contentResolver.query(
photographsUri,
PHOTOGRAPHS_PROJECTION,
null,
null,
sortOrder);
Log.d(TAG, "created the projection");
if (cursor != null && cursor.getCount() > 0) {
while (cursor.moveToNext()) {
photo_id = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID));
photo_title = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.TITLE));
photo_path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
// save to photo list
applicationPhotosList.add(new Photographs(photo_id, photo_title, photo_path));
Log.d(TAG, "new photo added");
}
cursor.close();
Log.d(TAG, "Photo array filled up");
}
else if (cursor == null){
Log.d(TAG, "No photos present, cursor is null");
Toast.makeText(this, "No photos present", Toast.LENGTH_SHORT).show();
}
Looks like trying to transform the path of getExternalFileDir() into an Uri doesn't work. The cursor is empty. But MediaStore only has MediaStore.Images.Media.EXTERNAL_CONTENT_URI and that's not suitable as that queries the whole device memory, while I only need that particular directory in which my app is saving the pictures taken. What am I missing? It cannot be that hard, right?
There is another way to retrieve the images from a specific folder w/o using the cursor and I think its more faster.
private fun getAllShownImagesPath(): ArrayList<String> {
var filePath: ArrayList<String> = ArrayList<String>()
val path = File(getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).path, "BacFo Camera")
if (path.exists()) {
for (i in path.list().iterator()) {
filePath?.add("" + path.toString() + "/" + i)
}
}
return filePath!!
}
`
In "fileNames" you have the path of all images or videos whatever you have saved in the specific folder
Related
In my app, the user can choose where the created files (text files) are created.
This part is working fine.
But now, I want to open an external "file explorer" app, pointing directly to the chosen folder.
The "file explorer " apps I know accept an absolute path as input (like /storage/emulated/0/Documents/test_folder)
When the user chooses a folder (with Intent.ACTION_OPEN_DOCUMENT_TREE), I get a content uri (like content://com.android.externalstorage.documents/tree/home%3Atest_folder)
Another example with an external sd card:
uri: content://com.android.externalstorage.documents/tree/3877-DB74%3ADocuments%2Ftest_folder
expected path: /storage/3877-DB74/Documents/test_folder
The uri points to a folder, not a file, so I can't use something like openInputStream
I have tried :
File f = new File(uri.getPath());
String path = f.getAbsolutePath();
but it gives: /tree/home:test_folder or /tree/3877-DB74:Documents/test_folder if on sd card
How can I get the real absolute path?
The code I use to call a file explorer:
Intent intent = new Intent(Intent.ACTION_VIEW);
String path = getExternalFilesDir(null).getAbsolutePath();
intent.setDataAndType(Uri.parse(path), "resource/folder");
if (intent.resolveActivityInfo(getPackageManager(), 0) != null)
{
startActivity(intent);
}
so basically you want to get file path from uri
you give try with this code
https://gist.github.com/pratikbutani/eb56f6f9f7013e31d8bfea9effbd4251
I have tried the suggested code (see above).
Unfortunately, I got an exception:
Caused by: java.lang.UnsupportedOperationException: Unsupported Uri content://com.android.externalstorage.documents/tree/home%3Atest_folder
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:167)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:418)
at android.content.ContentResolver.query(ContentResolver.java:760)
at android.content.ContentResolver.query(ContentResolver.java:710)
at android.content.ContentResolver.query(ContentResolver.java:668)
at ....UriUtils.getDataColumn(UriUtils.java:278)
Here is a copy of the code:
private static String getDataColumn(Context context, Uri uri)
{
Cursor cursor = null;
final String column = "_data";
final String[] projection = { column };
try {
cursor = context.getContentResolver().query(uri, projection,
null, null, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
I finally wrote my own method to get the absolute path for a folder from a Uri.
It is surely not fully generic, but it meets my need.
if it can help someone, here is my code:
Note: VOLUME_MAP is a map containing all mounted external volumes
/**************************************************************************/
public static String getRealPathFromContentUri(final Uri uri)
{
if (!isExternalStorageDocument(uri))
{
return null;
}
List<String> segs = uri.getPathSegments();
if (!"tree".equalsIgnoreCase(segs.get(0)))
{
return null;
}
String path = uri.getLastPathSegment();
final String[] split = path.split(":");
final String volumeId = split[0];
String userPath = "";
if (split.length > 1)
{
userPath = "/" + split[1];
}
if ("primary".equalsIgnoreCase(volumeId))
{
return Environment.getExternalStorageDirectory().getAbsolutePath() + userPath;
}
if ("home".equalsIgnoreCase(volumeId))
{
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + userPath;
}
// look for real volumeId
final String volumeName = VOLUME_MAP.get(volumeId);
if (volumeName == null)
{
return null;
}
path = "/storage";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
path = Environment.getStorageDirectory().getAbsolutePath();
}
return path + "/" + volumeId + userPath;
}
Thanks to all contributors on this topic.
We have an Android app where field workers take photographs which are stored on their phone and also uploaded via a web api.
If the uploads fail they do have retry mechanisms but sometimes they need to resort to pulling the images off their phone.
In order to bring the app up to Android 10 version without deprecation I was forced to write the images to an app internal directory.
The problem is that when they upgrade their app they lose their photos from the app.
(I do also copy the images to a backup directory but this is all looking a bit klutzy)
I would like to write the images to :
/storage/emulated/0/DCIM/GoTrialImages
Instead they are going to :
/storage/emulated/0/Android/data/au.come.aceware.ktr.ktr/files/DCIM/GoTrialImages/photoIntent
(where photoIntent is the activity that this is occurring in)
Here is the code I have copied and tweaked from an online article:
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String fileName = "JPEG_" + timeStamp + ".jpg";
File mediaStorageDir = new File(getExternalFilesDir(Environment.DIRECTORY_DCIM + File.separator +"GoTrialPhotos"), TAG);
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()){
Log.d(TAG, "failed to create directory");
}
// Return the file target for the photo based on filename
File file = new File(mediaStorageDir.getPath() + File.separator + fileName);
Uri bmpUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file);
Here is my file provider entry in the manifest:
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>```
and here is #xml/provider_paths:
```<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>```
1) Is it possible to do what I am seeking to do ?
2) How do I do it without using deprecated code
Many thanks in advance
Tony
Following the suggestion to use media store I kept most of the code for creating the app internal file name
(mainly because I wanted the randomised display name):
private File createImageFileV2() throws IOException
{
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
imageFileNameToUseAtWebServerEnd = strTrial + "_" + timeStamp + "_" + strUserId + ".jpg";
File[] storageDir = getExternalMediaDirs();
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir[0] /* directory */
);
return image;
}
I then passed the file object in to the following code:
public Uri testgetPhotoFileUri2(File f)
{
Uri uri = null;
String strDisplayName = f.getName();
final ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, strDisplayName);
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM );
final ContentResolver resolver = thisContext.getContentResolver();
try
{
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
uri = resolver.insert(contentUri, values);
if (uri == null)
throw new IOException("Failed to create new MediaStore record.");
return uri;
}
catch (IOException e)
{
if (uri != null) {
// Don't leave an orphan entry in the MediaStore
resolver.delete(uri, null, null);
}
}
return uri;
}
I then used the resulting uri as my camera uri:
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraUri);
However, when the OnActivityResult calls HandleBigCameraPhoto and attempts to extract the bitmap using the CameraUri:
private void handleBigCameraPhoto() {
Bitmap bitmap = null;
if (cameraUri != null)
{
if (Build.VERSION.SDK_INT >= 29) {
ImageDecoder.Source source = ImageDecoder.createSource(getApplicationContext().getContentResolver(), cameraUri);
try {
bitmap = ImageDecoder.decodeBitmap(source);
} catch (IOException e) {
e.printStackTrace();
}
It error traps to "no such file or directory"
Does this mean that I need to most of my work (image resizing, rotation, etc) using my app private file only and then as a last step insert the bitmap in to media store (and then delete the app private file so the user does not see the file name twice under gallery, recents)?
You will not make use of a deprecated function:
File file = new File(getExternalFilesDir(null)
.getParentFile()
.getParentFile()
.getParentFile()
.getParentFile(), "DCIM");
;-).
In my app I have created a option for taking Photo and Viewing taken Photos.
For taking Photos I am using default camera of mobile.
I am using below code for passing intent
public void intentForTakePicture() {
Intent picIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (picIntent.resolveActivity(getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
ex.printStackTrace();
Log.e(LOG_TAG,"Exception while trying to create image file!!");
}
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.app.photo.fileprovider",
photoFile);
picIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(picIntent, TAKE_PHOTO);
Log.d(LOG_TAG,"request code is: "+TAKE_PHOTO);
}
}
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir("Pictures/photo/");
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
currentPhotoPath = image.getAbsolutePath();
Log.d(LOG_TAG, "currentPhotoPath: " + currentPhotoPath);
return image;
}
In onActivityResult I am setting imageview whatever photos comes there
But the issue is, I am able to take photos but taken Photo is not getting back in onActivityResult.
Note: Getting this issue only for ASUS_X01BDA mobile.
I also checked the mobile camera permissions for my app. Got camera permission for my app.
I doesn't know the problem.
Please Anybody help me to find the cause and to solve it
I am developing an application that captures photo and saves it in internal device folder named "photo".I need to load the photo from "photo" folder to imageview.There is only one photo present in the folder.How to do this?
Please help me.Thank you in advance.
Below is the code that I have tried which loads photo from folder only if name of photo is displayed.
String path = Environment.getExternalStorageDirectory().getAbsolutePath()+ "/GeoPark/final_photo/20180504_002754.jpg";
File imgFile = new File(path);
if (imgFile.exists()) {
imageView.setImageBitmap(decodeFile(imgFile));
}
else
Toast.makeText(ViewActivity.this,"No Image File Present ",Toast.LENGTH_SHORT).show();
Try this method .. in below method give proper file path.
private void loadImageFromStorage(String path)
{
try {
File f=new File(path, "profile.jpg");
Bitmap b = BitmapFactory.decodeStream(new FileInputStream(f));
ImageView img=(ImageView)findViewById(R.id.imgPicker);
img.setImageBitmap(b);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
}
Second things ..
File file = ....
Uri uri = Uri.fromFile(file);
imageView.setImageURI(uri);
alos i hope you add below permission into android manifest file ..
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
You could simply do a search about this, guaranteed results.
Anyway, Glide will help you with that
EDIT :
Taking picture using camera:
private void dispatchTakePictureIntent(Context context) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(context.getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile(context);
} catch (IOException ex) {
// Error occurred while creating the File
Snackbar.make(btn_open_camera, "Couldn't create a file for your picture!", Snackbar.LENGTH_LONG);
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(context,
"com.example.android.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, MY_PERMISSIONS_REQUEST_CAMERA);
}
}
}
private File createImageFile(Context context) throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = context.getFilesDir();
Log.i(TAG, "StorageDir : " + storageDir);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
Log.i(TAG, "mCurrentPhotoPath : " + mCurrentPhotoPath);
return image;
}
Now you have the image path stored in mCurrentPhotoPath which is a String declared in current activity. As you said, you will need this in next activity, to do that you can take a look at this answer
My app selects an image taken by the system's camera and obtain its Uri from onActivityResult method, from here i would like to convert the Uri to android standard file path so that i will be able to check its orientation by passing the file path to Exifinterface's constructor and execute getAttributeInt to receive a value and then decide on how many degrees to rotate the image.
I found a sample code here on stackoverflow that has the capability to convert the image uri to file path. but the problem is, it uses DocumentContract class which is added in api level 19 onwards but my app needs to support lower version than API level 19. How can I do this? Or atleast have an alternative solution for getting the orientation of the image.
Use the following method to get the file path from the Uri. Here you need to pass the context and the uri and it maintains the compatibility for pre-Kitkat.
public String getRealPathFromURI(Context context, Uri contentUri) {
String res = "";
String[] proj = { MediaStore.Images.Media.DATA };
Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
res = cursor.getString(column_index);
}
cursor.close();
} else {
Log.d(TAG, "Cursor is null");
return contentUri.getPath();
}
return res;
}
Updated for Camera : The above solution is working for Uri returned for the Gallery Intent. For the camera intent use the below code.
public File getOutputMediaFile() {
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir;
// If the externam directory is writable then then return the External
// pictures directory.
if (isExternalStorageWritable()) {
mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
.getAbsolutePath() + File.separator + IConstants.CUSTOM_PROFILE_PIC_PATH);
} else {
mediaStorageDir = Environment.getDownloadCacheDirectory();
}
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
File mediaFile;
mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
return mediaFile;
}
Create a global variable selectedImage for storing the image path.
private Uri getOutputMediaFileUri() {
File mediaFile = Utilities.getInstance().getOutputMediaFile();
selectedImage = mediaFile.getAbsolutePath();
return Uri.fromFile(mediaFile);
}
Now call the Camera intent using the following method.
public void dispatchCameraIntent(View view) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// If there any applications that can handle this intent then call the intent.
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
Uri fileUri = getOutputMediaFileUri();
Log.d(TAG, "camera Uri : " + fileUri);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(takePictureIntent, CAMERA_PICKER);
}
}
In OnActivityResult use the selectedImage as the file path.