So i have this code for getting images with mediastore :
int dataIndex = mMediaStoreCursor.getColumnIndex(MediaStore.Files.FileColumns.DATA );
mMediaStoreCursor.moveToPosition(position);
String dataString = mMediaStoreCursor.getString(dataIndex);
Uri mediaUri = Uri.parse("file://" + dataString);
return mediaUri;
This good gets all the images in the pictures folder, i would like to change that to get all the images in a specific folder, which will be passed in as a string. e.g { android/picture/specificfolder/}
val DIRECTORY_NAME = "%your_folder_name%"
val selection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
MediaStore.MediaColumns.RELATIVE_PATH + " like ? "
else MediaStore.Images.Media.DATA + " like ? "
val selectionArgs = arrayOf(APP_RESOURCE_DIRECTORY_NAME)
val cursor = context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null,
selection,
selectionArgs,
null)
Check this post
It advice you to use cursor and pass the folder needed in the projection.
the relevant answer from teh post:
mediaCursor = getContentResolver().query( MediaStore.Files.getContentUri("external"),
null,
MediaStore.Images.Media.DATA + " like ? ",
new String[] {"%YOUR_FOLDER_NAME%"},
null);
You can use following method -
public List<String> getFromSdcard()
{
ArrayList<String> imagePaths = new ArrayList<String>();// list of file paths
File[] listFile;
File file= new
File(android.os.Environment.getExternalStorageDirectory(),"android/picture/specificfolder/");
if (file.isDirectory())
{
listFile = file.listFiles();
for (int i = 0; i < listFile.length; i++)
{
imagePaths.add(listFile[i].getAbsolutePath());
}
}
return imagePaths;
}
to retreive the images use
List<String> sample = getFromSdcard();
for(int i=0; i<sample.size() ; i++){
final Uri image = Uri.parse("file://"+sample(i).toString());
}
Related
I am trying to build a Pdf Scanner Android application for api level 21 and above, in the app I have an option to show and manage all the pdf files created by the application through the application.
I want to fetch Pdf files from Documents/PDFScanner folder and show them in a fragment. So far I am getting empty array but I have Pdf files in the folder.
I have tested the code on Android 11 and Android 8.1 devices.
here is my function to get pdf files
private void getPDFFilesFromDirecotry() {
Uri collection = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
collection = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
} else {
collection = MediaStore.Files.getContentUri("external");
}
String[] projection = {MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME};
Cursor mediaCursor = applicationContext.getContentResolver().query(collection,
projection,
MediaStore.MediaColumns.DATA + " LIKE?",
new String[]{Environment.DIRECTORY_DOCUMENTS + "/PDFScanner"},
null);
Log.e("TAG", "Message: " + mediaCursor.getCount());
if (mediaCursor == null || mediaCursor.getCount() <= 0 || !mediaCursor.moveToFirst()) {
Log.e("tag","No data");
return;
}
String mimetype = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf");
MediaScannerConnection.scanFile(applicationContext, new String[]{file.getPath()}, new String[]{mimetype}, null);
while (mediaCursor.moveToNext()) {
Log.e("TAG", "Message: ");
String path = mediaCursor.getString(mediaCursor.getColumnIndex(MediaStore.MediaColumns.BUCKET_DISPLAY_NAME));
Log.e("TAG", "getPDFFilesFromDirectory Path: " + path);
if (path != null) {
ivNoData.setVisibility(View.GONE);
file = new File(path);
String name = file.getName();
int position = name.lastIndexOf(".");
if (position > 0 && position < name.length() - 1) {
name = name.substring(0, position);
}
Date dateinMillis = new Date(file.lastModified());
#SuppressLint("SimpleDateFormat")
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yy hh.mm a");
String date = dateFormat.format(dateinMillis);
String size = formatSize(file.length());
viewFilesArray.add(new ViewFilesModel(path, name, size, date));
}
}
mediaCursor.close();
Log.e("TAG", "getPDFFilesFromDirectory: " + viewFilesArray.size());
filesAdapter.notifyDataSetChanged();
}
Thanks for the help.
I tried other solutions on Stack Overflow, but they are not working with my problem. The below code is working perfectly before Android Q versions, but did not work with Android Q.
Here are the functions which are used to fetch albums and images.
First for fetching AlbumImages:
public static ArrayList<ImageFolder> getPicturePaths(Context context){
ArrayList<ImageFolder> picFolders = new ArrayList<>();
ArrayList<String> picPaths = new ArrayList<>();
Uri allImagesuri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Images.ImageColumns.DATA, MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Images.Media.BUCKET_ID};
Cursor cursor = context.getContentResolver().query(allImagesuri, projection, null, null, null);
try {
if (cursor != null) {
cursor.moveToFirst();
}
do {
ImageFolder folds = new ImageFolder();
String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME));
String folder = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME));
String datapath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
//String folderpaths = datapath.replace(name,"");
String folderpaths = datapath.substring(0, datapath.lastIndexOf(folder + "/"));
folderpaths = folderpaths + folder + "/";
if (!picPaths.contains(folderpaths)) {
picPaths.add(folderpaths);
folds.setPath(folderpaths);
folds.setFolderName(folder);
folds.setFirstPic(datapath);//if the folder has only one picture this line helps to set it as first so as to avoid blank image in itemview
folds.addpics();
picFolders.add(folds);
} else {
for (int i = 0; i < picFolders.size(); i++) {
if (picFolders.get(i).getPath().equals(folderpaths)) {
picFolders.get(i).setFirstPic(datapath);
picFolders.get(i).addpics();
}
}
}
} while (cursor.moveToNext());
cursor.close();
} catch (Exception e) {
e.printStackTrace();
}
return picFolders;
}
And here is the method to fetch images from albums:
public static ArrayList<PhotoItemModel> getAllImagesByFolder(Context context, String path){
ArrayList<PhotoItemModel> images = new ArrayList<>();
Uri allImagesuri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = { MediaStore.Images.ImageColumns.DATA ,MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE};
Cursor cursor = context.getContentResolver().query( allImagesuri, projection, MediaStore.Images.Media.DATA + " like ? ", new String[] {"%"+path+"%"}, null);
try {
cursor.moveToFirst();
do{
PhotoItemModel pic = new PhotoItemModel();
pic.setPicturName(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)));
pic.setPicturePath(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)));
pic.setPictureSize(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)));
images.add(pic);
}while(cursor.moveToNext());
cursor.close();
ArrayList<PhotoItemModel> reSelection = new ArrayList<>();
for(int i = images.size()-1;i > -1;i--){
reSelection.add(images.get(i));
}
images = reSelection;
} catch (Exception e) {
e.printStackTrace();
}
return images;
}
How can I fix this?
First check whether the Android Q is getting some problem (permission related) to reach Folder path
String folderpaths = datapath.substring(0, datapath.lastIndexOf(folder + "/"));
folderpaths = folderpaths + folder + "/";
Under Android Q you cannot use the .DATA column.
Instead use .RELATIVE_PATH.
Read the threads tagged mediastore.
I have gone through a lot of posts but didn't find any answer that answers the question efficiently or even correctly. The closest I came was this How to avoid duplicate contact name (data ) while loading contact info to listview? but this has too much overhead. Is there any simpler or more efficient way to solve this?
I had the same problem you had: I was getting duplicate phone numbers. I solved this problem by obtaining the normalized number for each cursor entry and using a HashSet to keep track of which numbers I'd already found. Try this:
private void doSomethingForEachUniquePhoneNumber(Context context) {
String[] projection = new String[] {
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER,
//plus any other properties you wish to query
};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, null, null, null);
} catch (SecurityException e) {
//SecurityException can be thrown if we don't have the right permissions
}
if (cursor != null) {
try {
HashSet<String> normalizedNumbersAlreadyFound = new HashSet<>();
int indexOfNormalizedNumber = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER);
int indexOfDisplayName = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
int indexOfDisplayNumber = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
while (cursor.moveToNext()) {
String normalizedNumber = cursor.getString(indexOfNormalizedNumber);
if (normalizedNumbersAlreadyFound.add(normalizedNumber)) {
String displayName = cursor.getString(indexOfDisplayName);
String displayNumber = cursor.getString(indexOfDisplayNumber);
//haven't seen this number yet: do something with this contact!
} else {
//don't do anything with this contact because we've already found this number
}
}
} finally {
cursor.close();
}
}
}
After API 21 We Write this Query for remove contact duplicacy.
String select = ContactsContract.Data.HAS_PHONE_NUMBER + " != 0 AND " +
ContactsContract.Data.MIMETYPE
+ " = " + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "
AND "+ ContactsContract.Data.RAW_CONTACT_ID + " = " +
ContactsContract.Data.NAME_RAW_CONTACT_ID;
Cursor cursor = getContentResolver().query(ContactsContract.Data.CONTENT_URI, null, select,
null, null);
ContentResolver cr = this.getContentResolver();
String[] FieldList = {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER,ContactsContract.CommonDataKinds.Phone.CONTACT_ID};
Cursor c = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,FieldList,
null,null,ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
String name,phone,ContactID;
HashSet<String> normalizedNumbers = new HashSet<>();
if(c!=null)
{
while(c.moveToNext()!=false)
{
phone = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER));
if(normalizedNumbers.add(phone)==true)
{
name = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
ContactID = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
MyContacts m = new MyContacts(name,phone,ContactID);
ContactList.add(m);
}
}
c.close();
I'm trying to scan all the music files from a specified folder and I am encountering a performance problem combined with an app close.
My approach is to first get all the music files from the phone using:
String[] projection = {
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.TITLE
};
String selection = MediaStore.Audio.Media.IS_MUSIC + "!=" + 0;
cursor = managedQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
MediaStore.Audio.Media.TITLE + " ASC");
Then I get the location for each of the fetched files using:
columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
If the file location is within a specified folder, I add it to the list that will be displayed on screen.
The problem with this approach is that it is not performing well.
It hangs for 4-5 seconds.
If I scroll the list the app closes.
Below is the full code I'm using right now:
private String[] getTrackList() {
String[] result = null;
String[] projection = {
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.TITLE
};
String selection = MediaStore.Audio.Media.IS_MUSIC + "!=" + 0;
cursor = managedQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
MediaStore.Audio.Media.TITLE + " ASC");
result = new String[cursor.getCount()];
int position = 0;
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
getSearchableDir("/mnt/sdcard/Music/"); //See below
columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
String currentDir = cursor.getString(columnIndex).substring(0,
cursor.getString(columnIndex).lastIndexOf("/") + 1);
if (searchableDir.contains(currentDir)) {
columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE);
result[position] = cursor.getString(columnIndex);
position++;
}
}
return result;
}
// Function for recursively fetching all the sub-directories
private void getSearchableDir(String path) {
searchableDir.add(path);
File root = new File(path);
File[] list = root.listFiles();
for (File f : list) {
if (f.isDirectory()) {
searchableDir.add(f.getAbsolutePath());
getSearchableDir(f.getAbsolutePath());
}
}
}
I think it's safe to say that getSearchableDir is the performance hog here.
Re-factor the code so that this function is not called.
In our app we want to get the music-files of one specific folder. We want to do this, to use the media store.
So we have a path /mnt/sd/music of this dir we will need all the files and not of the subdirs.
We tried to make a query with a substring, but it doesnt work.
public ArrayList<Song> getAudioFilesOfDir(String pathDirectory)
{
ArrayList<Song> songs = new ArrayList<Song>();
//Some audio may be explicitly marked as not being music
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0 AND " + android.provider.MediaStore.Audio.Media.DATA.substring(pathDirectory.length()).contains("/") ;
String[] projection = {
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.TRACK,
MediaStore.Audio.Media.YEAR,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.ALBUM_KEY,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.TITLE_KEY,
MediaStore.Audio.Media.ARTIST_ID,
MediaStore.Audio.Media.ARTIST
};
ContentResolver cr = CoreLib.Context().getContentResolver();
Cursor cursor = cr.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
MediaStore.Audio.Media._ID);
int _ID_Column = cursor.getColumnIndex(MediaStore.Audio.Media._ID);
int DATA_Column = cursor.getColumnIndex(MediaStore.Audio.Media.DATA);
int TRACK_Column = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
int YEAR_Column = cursor.getColumnIndex(MediaStore.Audio.Media.YEAR);
int DURATION_Column = cursor.getColumnIndex(MediaStore.Audio.Media.DURATION);
int ALBUM_ID_Column = cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID);
int ALBUM_Column = cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM);
//int ALBUM_KEY_Column = cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_KEY);
//int TITLE_Column = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
//int TITLE_KEY_Column = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE_KEY);
//int ARTIST_ID_Column = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST_ID);
int ARTIST_Column = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
while(cursor.moveToNext())
{
Song song = new Song();
song.setTrackId(cursor.getInt(_ID_Column));
song.setPathAudioFile(cursor.getString(DATA_Column));
song.setTrackName(cursor.getString(TRACK_Column));
song.setYearTrack(cursor.getInt(YEAR_Column));
song.setDuration(cursor.getString(DURATION_Column));
song.setAlbumId(cursor.getInt(ALBUM_ID_Column));
song.setAlbumName(cursor.getString(ALBUM_Column));
song.setArtist(cursor.getString(ARTIST_Column));
songs.add(song);
}
return songs;
}
I can tell that this won't work:
android.provider.MediaStore.Audio.Media.DATA.substring(pathDirectory.length()).contains("/")
MediaStore.Audio.Media.DATA just contains a constant for database column and operating on that is just wrong.
You might find your solution in these answers.