I am using ActivityResultContracts.OpenDocumentTree() to allow the user to select the folder where they copied several folders which have a list of image files (the image files may be several folders deep from the folder they selected). I need to search for a known folder name and retrieve the image files. I'm trying to do it so that the user does not have to select each of the image files. I can use other data to search and find the correct folder(s) and image files once the "root" folder is know. The problem I am having is trying to convert the Uri returned from the ActivityResult into a File (directory) to search for the sub-folder. The resulting "File" is not a valid File. Maybe this is not the correct way to do this? I would like to support Build.VERSION_CODES.Q and earlier versions of Android. Is this the wrong approach?
Here is what I have tried. In this sample, I'm not doing anything yet with the files since I'm not getting them.
Fragment
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
mViewModel = new ViewModelProvider(this).get(ImportImagesViewModel.class);
Uri uri = Uri.parse("");
selectImageFolder.launch(uri);
}
ActivityResultLauncher<Uri> selectImageFolder = registerForActivityResult(
new ActivityResultContracts.OpenDocumentTree(), new ActivityResultCallback<Uri>()
{
#Override
public void onActivityResult(Uri result)
{
try {
if (result != null) {
for (MetalItem metalItem : MetalsGlobal.GetMetalItems()) {
getImageFilesFromImageFolder(new File(result.getPath()), metalItem.Source, metalItem.OrderNumber, String.valueOf(metalItem.OrderItemNumber));
}
} else {
Toast.makeText(requireContext(), "Nothing selected...", Toast.LENGTH_LONG).show();
}
} catch (Exception x) {
Log.e("Import Inventory Get File Launcher", x.getMessage(), x);
Toast.makeText(requireContext(), x.getMessage(), Toast.LENGTH_LONG).show();
}
}
});
private void getImageFilesFromImageFolder(File folder, #NonNull String source, #NonNull String orderNumber, #NonNull String itemNumber)
{
if (folder == null ||
source.length() == 0 ||
orderNumber.length() == 0 ||
itemNumber.length() == 0)
return;
File[] sourceFolders = folder.listFiles();
if (sourceFolders == null) return;
for (File sourceFolder : sourceFolders) {
if (!sourceFolder.isDirectory())
continue;
if (!sourceFolder.toString().equalsIgnoreCase(source))
continue;
File[] orderNumberFolders = sourceFolder.listFiles();
if (orderNumberFolders == null) return;
for (File orderNumberFolder : orderNumberFolders) {
if (!orderNumberFolder.isDirectory())
continue;
if (!orderNumberFolder.toString().equalsIgnoreCase(orderNumber))
continue;
File[] itemNumberFolders = orderNumberFolder.listFiles();
if (itemNumberFolders == null) return;
for (File itemNumberFolder : itemNumberFolders) {
if (!itemNumberFolder.isDirectory())
continue;
if (!itemNumberFolder.toString().equalsIgnoreCase(itemNumber))
continue;
// Here should be the image files
File[] imageFiles = itemNumberFolder.listFiles();
if (imageFiles == null) return;
for (File imageFile : imageFiles) {
if (!(imageFile.isFile() && imageFile.canRead()))
return;
try {
Uri imageUri = Uri.parse(imageFile.toString());
int blah = imageUri.toString().length();
} catch (Exception x) {
return;
}
}
}
}
}
}
Based on the suggestion from CommonsWare this is the revised method which works exactly like I need.
private void processImageFolder(DocumentFile folder, MetalItem metalItem)
{
String source = metalItem.Source;
String orderNumber = metalItem.OrderNumber;
String itemNumber = String.valueOf(metalItem.OrderItemNumber);
if (folder == null ||
source.length() == 0 ||
orderNumber.length() == 0 ||
itemNumber.length() == 0)
return;
DocumentFile[] sourceFolders = folder.listFiles();
for (DocumentFile sourceFolder : sourceFolders) {
if (!sourceFolder.isDirectory())
continue;
if (!Objects.requireNonNull(sourceFolder.getName()).equalsIgnoreCase(source))
continue;
DocumentFile[] orderNumberFolders = sourceFolder.listFiles();
for (DocumentFile orderNumberFolder : orderNumberFolders) {
if (!orderNumberFolder.isDirectory())
continue;
if (!Objects.requireNonNull(orderNumberFolder.getName()).equalsIgnoreCase(orderNumber))
continue;
DocumentFile[] itemNumberFolders = orderNumberFolder.listFiles();
for (DocumentFile itemNumberFolder : itemNumberFolders) {
if (!itemNumberFolder.isDirectory())
continue;
if (!Objects.requireNonNull(itemNumberFolder.getName()).equalsIgnoreCase(itemNumber))
continue;
// Here should be the image files
DocumentFile[] imageFiles = itemNumberFolder.listFiles();
for (DocumentFile imageFile : imageFiles) {
if (!(imageFile.isFile() && imageFile.canRead()))
return;
try {
loadItemImage(metalItem, imageFile);
} catch (Exception x) {
return;
}
}
return;
}
}
}
}
I am able to fetch all the images from any specified non hidden folder from device but how can I get all the images from a hidden specified folder.
As soon as I mention my hidden folder name in the query, cursor return null
public static List<MediaData> getAppScannedImages(Context context) {
Cursor imagecursor = null;
List<MediaData> gallerydata = new ArrayList<MediaData>();
try {
final String orderBy = Images.ImageColumns.DATE_TAKEN + " DESC";
imagecursor = context.getContentResolver()
.query(Images.Media.EXTERNAL_CONTENT_URI,
projectionImage,
Images.Media.BUCKET_DISPLAY_NAME + "='"
+ ".myHiddenFolder" + "'", null,
orderBy);
if (imagecursor != null) {
imagecursor.moveToFirst();
int count = imagecursor.getCount();
for (int i = 0; i < count; i++) {
MediaData galData = new MediaData();
galData.setKey_id(i);
galData.setId(imagecursor.getString(0));
galData.setName(imagecursor.getString(1));
galData.setPath(imagecursor.getString(2));
galData.setDate(imagecursor.getString(3));
gallerydata.add(galData);
imagecursor.moveToNext();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (imagecursor != null) {
imagecursor.close();
}
}
return gallerydata;
}
You can try a different approach.
You have to find out the list of hidden folder from sd card and search all those folders for images.
the follwoing code is displays hidden files:
public void goTODir(File dir) {
//dir is initail dir like="/mnt/sdcard"
String imageType = ".jpg";
File[] listFile = dir.listFiles();
if (listFile != null) {
for (int i = 0; i < listFile.length; i++) {
if (listFile[i].isDirectory()) {
goTODir(listFile[i]);
} else {
if (listFile[i].isHidden()){
if(listFile[i].getName().endsWith(imageType))
{
//add to your array list
}
}
}
}
}
}
String path = Environment.getExternalStorageDirectory().toString();
File dir = new File(path);
File listFile[] = dir.listFiles();
for (int i = 0; i < listFile.length; i++) {
if(listFile[i].getAbsolutePath().contains("your hidden folder name")){
File dirtest = new File(listFile[i].getAbsolutePath());
File listFiletest[] = dirtest.listFiles();
for (int j = 0; j < listFiletest.length; j++) {
get all images from hidden folder
}
}
}
For Kotlin Lover
companion object {
const val FOLDER_PATH = "/YourFolder/.hideen/"
}
/**
* Method to get all Image Path
* #return [ArrayList]
* */
fun getImagePath(): ArrayList<String> {
// image path list
val list: ArrayList<String> = ArrayList()
// fetching file path from storage
val file = File(Environment.getExternalStorageDirectory().toString() + FOLDER_PATH)
val listFile = file.listFiles()
if (listFile != null && listFile.isNullOrEmpty()) {
Arrays.sort(listFile, LastModifiedFileComparator.LASTMODIFIED_REVERSE)
}
if (listFile != null) {
for (imgFile in listFile) {
if (
imgFile.name.endsWith(".jpg")
|| imgFile.name.endsWith(".jpeg")
|| imgFile.name.endsWith(".png")
) {
val model : String = imgFile.absolutePath
list.add(model)
}
}
}
// return imgPath List
return list
}
Though i got so many post but problem is that
it return true if phone has inbuild storage.
Anyone for help me
Below code will helps...
/**
* Returns all available external SD-Card roots in the system.
*
* #return paths to all available external SD-Card roots in the system.
*/
public static String[] getStorageDirectories() {
String[] storageDirectories;
String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
List<String> results = new ArrayList<String>();
File[] externalDirs = myContext.getExternalFilesDirs(null);
for (File file : externalDirs) {
String path = null;
try {
path = file.getPath().split("/Android")[0];
} catch (Exception e) {
e.printStackTrace();
path = null;
}
if (path != null) {
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Environment.isExternalStorageRemovable(file))
|| rawSecondaryStoragesStr != null && rawSecondaryStoragesStr.contains(path)) {
results.add(path);
}
}
}
storageDirectories = results.toArray(new String[0]);
} else {
final Set<String> rv = new HashSet<String>();
if (!TextUtils.isEmpty(rawSecondaryStoragesStr)) {
final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
Collections.addAll(rv, rawSecondaryStorages);
}
storageDirectories = rv.toArray(new String[rv.size()]);
}
return storageDirectories;
}
//To check external SD is available or not
String retArray[] = getStorageDirectories();
if (retArray.length == 0) {
Toast.makeText(ListenActivity.this, "Sdcard not Exists", Toast.LENGTH_SHORT).show();
} else {
for (int i = 0; i < retArray.length; i++) {
Log.e("path ", retArray[i]);
}
}
I am using native Android camera and save file to my application data folder (/mnt/sdcard/Android/data/com.company.app/files/Pictures/). At the same time anther copy of photo is saved to DCIM folder.
This is my code:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
String formattedImageName = getDateString() + ".jpg";
File image_file = new File(this.getExternalFilesDir(Environment.DIRECTORY_PICTURES), formattedImageName);
Uri imageUri = Uri.fromFile(image_file);
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
startActivityForResult(intent, REQUEST_FROM_CAMERA);
How can I prevent saving additional copy of image to DCIM folder?
Many Thanks
You can use the following :
First we get the last saved image by checking which was the last modified image. Then check if last modified time is in the last few seconds. You may also have to check the exact location of where camera stores the image.
private boolean deleteLastFromDCIM() {
boolean success = false;
try {
File[] images = new File(Environment.getExternalStorageDirectory()
+ File.separator + "DCIM/Camera").listFiles();
File latestSavedImage = images[0];
for (int i = 1; i < images.length; ++i) {
if (images[i].lastModified() > latestSavedImage.lastModified()) {
latestSavedImage = images[i];
}
}
// OR JUST Use success = latestSavedImage.delete();
success = new File(Environment.getExternalStorageDirectory()
+ File.separator + "DCIM/Camera/"
+ latestSavedImage.getAbsoluteFile()).delete();
return success;
} catch (Exception e) {
e.printStackTrace();
return success;
}
}
Unfortunately, some smart phones save images in another folder such as DCIM/100MEDIA. So can't rely to these solution. I prefer use this way:
String[] projection = new String[] {
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE};
final Cursor cursor = managedQuery(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,projection, null, null,
MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if(cursor != null){
cursor.moveToFirst();
// you will find the last taken picture here and can delete that
}
I tried to find out if a second copy exists and delete the copy. I used the above code to find the last taken picture.
Notice: Don't use cursor.close(); after using managedQuery, Leave the cursor for the Android system to manage and don't call that. You can see managedQuery()
Notice2: The managedQuery method is deprecated and it should be avoided, implement CursorLoaders instead.
check this code..
private void FillPhotoList() {
// initialize the list!
GalleryList.clear();
String[] projection = { MediaStore.Images.ImageColumns.DISPLAY_NAME };
for(int i=0;i<projection.length;i++)
Log.i("InfoLog","projection "+projection[0].toString());
// intialize the Uri and the Cursor, and the current expected size.
Cursor c = null;
Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Log.i("InfoLog","FillPhoto Uri u "+u.toString());
// Query the Uri to get the data path. Only if the Uri is valid.
if (u != null)
{
c = managedQuery(u, projection, null, null, null);
}
// If we found the cursor and found a record in it (we also have the id).
if ((c != null) && (c.moveToFirst()))
{
do
{
// Loop each and add to the list.
GalleryList.add(c.getString(0)); // adding all the images sotred in the mobile phone(Internal and SD card)
}
while (c.moveToNext());
}
Log.i(INFOLOG,"gallery size "+ GalleryList.size());
}
and this is where the method is doing all magic
/** Method will check all the photo is the gallery and delete last captured and move it to the required folder.
*/
public void movingCapturedImageFromDCIMtoMerchandising()
{
// This is ##### ridiculous. Some versions of Android save
// to the MediaStore as well. Not sure why! We don't know what
// name Android will give either, so we get to search for this
// manually and remove it.
String[] projection = { MediaStore.Images.ImageColumns.SIZE,
MediaStore.Images.ImageColumns.DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATA,
BaseColumns._ID,};
// intialize the Uri and the Cursor, and the current expected size.
for(int i=0;i<projection.length;i++)
Log.i("InfoLog","on activityresult projection "+projection[i]);
//+" "+projection[1]+" "+projection[2]+" "+projection[3] this will be needed if u remove the for loop
Cursor c = null;
Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Log.i("InfoLog","on activityresult Uri u "+u.toString());
if (CurrentFile != null)
{
// Query the Uri to get the data path. Only if the Uri is valid,
// and we had a valid size to be searching for.
if ((u != null) && (CurrentFile.length() > 0))
{
//****u is the place from data will come and projection is the specified data what we want
c = managedQuery(u, projection, null, null, null);
}
// If we found the cursor and found a record in it (we also have the size).
if ((c != null) && (c.moveToFirst()))
{
do
{
// Check each area in the gallery we built before.
boolean bFound = false;
for (String sGallery : GalleryList)
{
if (sGallery.equalsIgnoreCase(c.getString(1)))
{
bFound = true;
Log.i("InfoLog","c.getString(1) "+c.getString(1));
break;
}
}
// To here we looped the full gallery.
if (!bFound) //the file which is newly created and it has to be deleted from the gallery
{
// This is the NEW image. If the size is bigger, copy it.
// Then delete it!
File f = new File(c.getString(2));
// Ensure it's there, check size, and delete!
if ((f.exists()) && (CurrentFile.length() < c.getLong(0)) && (CurrentFile.delete()))
{
// Finally we can stop the copy.
try
{
CurrentFile.createNewFile();
FileChannel source = null;
FileChannel destination = null;
try
{
source = new FileInputStream(f).getChannel();
destination = new FileOutputStream(CurrentFile).getChannel();
destination.transferFrom(source, 0, source.size());
}
finally
{
if (source != null)
{
source.close();
}
if (destination != null)
{
destination.close();
}
}
}
catch (IOException e)
{
// Could not copy the file over.
ToastMaker.makeToast(this, "Error Occured", 0);
}
}
//****deleting the file which is in the gallery
Log.i(INFOLOG,"imagePreORNext1 "+imagePreORNext);
Handler handler = new Handler();
//handler.postDelayed(runnable,300);
Log.i(INFOLOG,"imagePreORNext2 "+imagePreORNext);
ContentResolver cr = getContentResolver();
cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, BaseColumns._ID + "=" + c.getString(3), null);
break;
}
}
while (c.moveToNext());
}
}
}
A nice solution by Parth. But it's good for Samsungs that keep images in DCIM/Camera. Some phones - Sony Ericssons, HTCs keep them in folders like DCIM/100MEDIA, DCIM/100ANDRO so I have slightly modified the code:
private boolean deleteLastFromDCIM() {
boolean success = false;
try {
//Samsungs:
File folder = new File(Environment.getExternalStorageDirectory() + File.separator + "DCIM/Camera");
if(!folder.exists()){ //other phones:
File[] subfolders = new File(Environment.getExternalStorageDirectory() + File.separator + "DCIM").listFiles();
for(File subfolder : subfolders){
if(subfolder.getAbsolutePath().contains("100")){
folder = subfolder;
break;
}
}
if(!folder.exists())
return false;
}
File[] images = folder.listFiles();
File latestSavedImage = images[0];
for (int i = 1; i < images.length; ++i) {
if (images[i].lastModified() > latestSavedImage.lastModified()) {
latestSavedImage = images[i];
}
}
success = latestSavedImage.delete();
return success;
} catch (Exception e) {
e.printStackTrace();
return success;
}
}
I am encountering a similar problem with the Moto Z Force (7.1.1). I have the MediaStore.EXTRA_OUTPUT defined on the intent, but a duplicate file is still created in the camera directory.
I need to test on other devices, but here's an approach I took regarding this issue. Rather than trying to find the specific camera directory, I'm using the MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME location.
Here's my code snippet:
private void removeCameraDuplicate() {
String[] proj = {
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns._ID };
String selection = MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME + " = ? ";
String[] selectionArgs = new String[] { "Camera" };
Cursor cursor = mActivity.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, proj, selection, selectionArgs, MediaStore.Images.ImageColumns.DATE_TAKEN + " desc");
if (cursor != null) {
int idxPath = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (cursor.getCount() > 0 && idxPath > -1 && cursor.moveToFirst()) {
File original = new File(mMediaPath);
File cameraDupe = new File(cursor.getString(idxPath));
if (original.exists() && cameraDupe.exists()) {
LogUtils.LOGE("***> camera", "original " + original.length());
LogUtils.LOGE("***> camera", "original " + original.lastModified());
LogUtils.LOGE("***> camera", "duplicate " + cameraDupe.length());
LogUtils.LOGE("***> camera", "duplicate " + cameraDupe.lastModified());
if (original.length() == cameraDupe.length() && original.lastModified() == cameraDupe.lastModified()) {
if (cameraDupe.delete()) {
LogUtils.LOGE("***> camera", "duplicate deleted");
}
}
}
}
cursor.close();
}
}
I am trying to read all the images in the SDCARD with the Directory in which its present. e.g: if there is a file TEST.jpg in /mnt/sdcard/album1 and TEST2.jpg in /mnt/sdcard/album1/album2 i should be able to get the directory name album1 and album2.
I have written a code which does this in recursive manner, This works when the no of folders are less but when the number of directories increases the loop just come out of it.
public void getImageFoldes(String filepath){
String albumpath;
File file = new File(filepath);
File[] files = file.listFiles();
for (int fileInList = 0; fileInList < files.length; fileInList++)
{
File filename;
filename =files[fileInList];
if(filename.isHidden()|| filename.toString().startsWith("."))
return;
if (filename.isDirectory()){
albumpath = filename.toString();
String[] split;
String title;
split= albumpath.split("/");
title=split[split.length-1];
result = new thumbnailResults();
result.setTitle(title);
result.setPath(albumpath);
result.setIsLocal(true);
//result.setCreated("05-06-2011");
getImageFoldes(filename.toString());
}
else{
if (files.length !=0)
{
//if File is the image file then store the album name
if ((files[fileInList].toString()).contains(".png")||
(files[fileInList].toString()).contains(".jpg")||
(files[fileInList].toString()).contains(".jpeg")){
if (!results.contains(result)){
result.setUri(Uri.parse(files[fileInList].getPath()));
results.add(result);
myadapter.notifyDataSetChanged();
}
}
}
}
}
}
Use the following code.
to get the path of all images and directories from sdcard.
public static ArrayList<String> getPathOfAllImages(Activity activity) {
ArrayList<String> absolutePathOfImageList = new ArrayList<String>();
String absolutePathOfImage = null;
String nameOfFile = null;
String absolutePathOfFileWithoutFileName = null;
Uri uri;
Cursor cursor;
int column_index;
int column_displayname;
int lastIndex;
// absolutePathOfImages.clear();
uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = { MediaColumns.DATA,
MediaColumns.DISPLAY_NAME };
cursor = activity.managedQuery(uri, projection, null, null, null);
column_index = cursor.getColumnIndexOrThrow(MediaColumns.DATA);
column_displayname = cursor
.getColumnIndexOrThrow(MediaColumns.DISPLAY_NAME);
// cursor.moveToFirst();
while (cursor.moveToNext()) {
// for(int i=0; i<cursor.getColumnCount();i++){
// Log.i(TAG,cursor.getColumnName(i)+".....Data Present ...."+cursor.getString(i));
// }
// Log.i(TAG,"=====================================");
absolutePathOfImage = cursor.getString(column_index);
nameOfFile = cursor.getString(column_displayname);
lastIndex = absolutePathOfImage.lastIndexOf(nameOfFile);
lastIndex = lastIndex >= 0 ? lastIndex
: nameOfFile.length() - 1;
absolutePathOfFileWithoutFileName = absolutePathOfImage
.substring(0, lastIndex);
if (absolutePathOfImage != null) {
absolutePathOfImageList.add(absolutePathOfImage);
}
}
// Log.i(TAG,"........Detected images for Grid....."
// + absolutePathOfImageList);
return absolutePathOfImageList;
}
To get all the image files from the Sdcard, it may work.
public class ReadallImagesActivity extends Activity {
ArrayList<String> arlist = new ArrayList<String>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
File ff = Environment.getExternalStorageDirectory();
loadImagepaths(ff);
setContentView(R.layout.main);
Toast.makeText(ReadallImagesActivity.this, "Array size == " +arlist.size(), Toast.LENGTH_LONG).show();
}
public void loadImagepaths(File file) {
for (File f : file.listFiles()) {
if (f.isDirectory()) {
if (f.getAbsolutePath().endsWith(".android_secure")) {
break;
}
if (f.getAbsolutePath().endsWith("DCIM")) {
continue;
}
loadImagepaths(f);
} else {
if (f.getAbsolutePath().endsWith(".png") ||
f.getAbsolutePath().endsWith(".gif") ||
f.getAbsolutePath().endsWith(".jpg"))
{
arlist.add(f.getAbsolutePath());
}
}
}
}
}