Android : Images and video path in same cursor without cursorLoader - android

I want to show video and images from memory using content resolver but i don't want to use cursorLoader for it.
I am using below code to get uri of file but it only returns images but i want images as well as videos uri.
private fun getAllShownPath(activity:Activity):ArrayList<String> {
val uri:Uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
var cursor:Cursor?= null
val columnIndexData:Int
val columnIndexFolderName:Int
val listOfAllImages = ArrayList<String>()
var absolutePathOfImage:String? = null
val projection = arrayOf<String>(MediaStore.MediaColumns.DATA, MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Video.Media.BUCKET_DISPLAY_NAME)
cursor = activity.contentResolver.query(uri, projection, null, null, null)
columnIndexData = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)
columnIndexFolderName = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME)
while (cursor.moveToNext())
{
absolutePathOfImage = cursor.getString(columnIndexData)
listOfAllImages.add(absolutePathOfImage)
}
return listOfAllImages
}
What i am doing wrong here ?

String[] projection = {
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.DATE_ADDED,
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.MIME_TYPE,
MediaStore.Files.FileColumns.TITLE
};
// This will Return only video and image metadata.
String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
+ " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO;
Uri queryUri = MediaStore.Files.getContentUri("external");
CursorLoader cursorLoader = new CursorLoader(
this,
queryUri,
projection,
selection,
null, // Selection args (none).
MediaStore.Files.FileColumns.DATE_ADDED + " DESC" // Sort order.
);

Related

How to use limit and offset to restrict the data by content resolver (android)?

Uri uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.MediaColumns._ID, MediaStore.MediaColumns.SIZE, MediaStore.Images.ImageColumns.DATE_MODIFIED};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Bundle bundle = new Bundle();
// Sort function
bundle.putStringArray(
ContentResolver.QUERY_ARG_SORT_COLUMNS,
new String[]{MediaStore.Images.ImageColumns.DATE_MODIFIED}
);
bundle.putInt(
ContentResolver.QUERY_ARG_SORT_DIRECTION,
ContentResolver.QUERY_SORT_DIRECTION_DESCENDING
);
bundle.putInt(ContentResolver.QUERY_ARG_LIMIT, limit);
bundle.putInt(ContentResolver.QUERY_ARG_OFFSET, offset);
cursor = this.getContentResolver().query(uri, projection, bundle, null);
} else {
cursor = this.getContentResolver().query(uri, projection,
null, null
, MediaStore.MediaColumns.DATE_MODIFIED + " DESC " + " LIMIT " + limit + " OFFSET " + offset, null);
}
I am using this query to get images restricted by limit and offset. Though no matter what, this is always returning a cursor count of 4546. that means the data is not at all limited and the query is not working properly.

Android Q: How to get a list of images from a specific directory

Android Q: I need to get a list of images from a specific directory I saved images on it and display these images on my app.
Save images code:
final String relativeLocation = Environment.DIRECTORY_PICTURES + File.separator + "MyMedia" + File.separator + "Photo";
ContentResolver resolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
fos = resolver.openOutputStream(Objects.requireNonNull(imageUri));
bmp.compress(Bitmap.CompressFormat.JPEG,100, fos);
Objects.requireNonNull(fos).close();
Objects.requireNonNull(fos).flush();
Get images code:
Uri externalUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {
MediaStore.Files.FileColumns._ID,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.MediaColumns.TITLE,
MediaStore.Images.Media.MIME_TYPE,
MediaStore.MediaColumns.RELATIVE_PATH
};
Cursor cursor = context.getContentResolver().query(externalUri, projection, null, null, MediaStore.Images.Media.DATE_TAKEN +" DESC");
int idColumn = cursor.getColumnIndex(MediaStore.MediaColumns._ID);
int titleColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.TITLE);
int relativePathColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.RELATIVE_PATH);
while (cursor.moveToNext()) {
Uri photoUri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getString(idColumn));
}
I need a direct query to get list of all images saved in the RELATIVE_PATH , ("MyMedia/Photo") without add, if condition in the cursor loop to check relativePathColumn
if equal "MyMedia/Photo", because of this loop for all images in the device of the user!
Did we have any way to get a list of images directly from my directory?
String path = "MyMedia/Photo";
String selection = MediaStore.Files.FileColumns.RELATIVE_PATH + " like ? " ;
String selectionargs []= new String[]{"%" + path + "%"};
.query(externalUri, projection, selection, selectionars, MediaStore.Images.Media.DATE_TAKEN

Invalid URI exception in DocumentsContract.getDocumentId (uri)

I am trying to get the document id of the image which I saved, I am saving the image file to the following external storage in the device.
file:///storage/emulated/0/Pictures/BeautifulFaces/kvh_20160420T225141.jpg
After querying querying
public String checkIfImageIsRotated(Uri image_uri){
Log.i(LOG_TAG, ">>>>> START ");
int rotation =0;
String[] selection = new String[] { MediaStore.Images.Media._ID,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media.ORIENTATION};
String selectionArgs = MediaStore.Images.Media.BUCKET_DISPLAY_NAME + " = ?";
String[] args = new String[]{FOLDER_NAME};
String sortOrder = MediaStore.Images.Media.DATE_TAKEN + " DESC";
ContentResolver content = mContext.getContentResolver();
Cursor mediaCursor = content.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
selection ,
selectionArgs,
args,
sortOrder);
//select of columns
//MediaStore.Images.ImageColumns.ORIENTATION
//MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME
if (mediaCursor != null && mediaCursor.getCount() !=0 ) {
if(mediaCursor.moveToNext()){
Log.i(LOG_TAG, ">>>>> media cursor not null ");
String orientation = mediaCursor.getString(4);
String bucket_display_name = mediaCursor.getString(1);
image_data_path = mediaCursor.getString(2);
Log.i(LOG_TAG, ">>>>> rotation - " + orientation + ", bucket display name - " + bucket_display_name + ", data - " + image_data_path);
}
mediaCursor.close();
}
Log.i(LOG_TAG, ">>>>> END ");
return image_data_path;
}
image_data_path is
/storage/emulated/0/Pictures/BeautifulFaces/kvh_20160420T225141.jpg
Now when I am trying to do DocumentsContract.getDocumentId(uri) where uri is either the file uri mentioned at the top or image_data_path returned from the cursor, in both cases I am getting the following error.
Caused by: java.lang.IllegalArgumentException: Invalid URI: file:///storage/emulated/0/Pictures/BeautifulFaces/kvh_20160420T225141.jpg
or
Caused by: java.lang.IllegalArgumentException: Invalid URI: /storage/emulated/0/Pictures/BeautifulFaces/kvh_20160420T225141.jpg
Cannot understand why Document.getDocumentId is not able to parse the Uri.
Thanks

Android: Contact list has duplicate names

I have a contact list in a sort order. But in my contact list the name is duplicating with same number. I think the issue is because of the contact list sync with different account.
I check with Hash map. But when I using hash map the result is not sorted with name .
private static final String[] PROJECTION = new String[] {
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER
};
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION,
null, null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " COLLATE NOCASE ASC");
if (cursor != null) {
try {
int nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
String nameContact = cursor.getString(nameIndex);
finally {
cursor.close();
}
}
Adapter
holder.name.setText(itemListPogo.get(position).getItemName());
Can anyone please help to avoid the duplication in name.
You're seeing duplicate contacts because they belong to different accounts. i.e. Same number can show up 3 times if it is synced with Facebook, WhatsApp, and Google account. You can find more information here Android Account Manager
This is how you can use column ContactsContract.RawContacts.ACCOUNT_TYPE to filter and retrieve contacts associated with a single account only.
String[] projection = new String[] {
ContactsContract.RawContacts._ID,
ContactsContract.RawContacts.ACCOUNT_TYPE,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.PHOTO_URI,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Photo.CONTACT_ID };
String selectionFields = ContactsContract.RawContacts.ACCOUNT_TYPE + " = ?";
String[] selectionArgs = new String[]{"com.google"};
Cursor cursor = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
projection,
selectionFields,
selectionArgs,
ContactsContract.Contacts.DISPLAY_NAME
);
In this code, only contacts that are associated with Google Account are selected. Similarly, if you want to get list of WhatsApp Contacts only you can replace "com.google" with "com.whatsapp"
I will recommend you to use where my searching ends, it will give you fastest result.
public static List<ContactDTO> getPhone(Context context) {
List<ContactDTO> contactList = new ArrayList<ContactDTO>();
ContentResolver cr = context.getContentResolver();
String[] PROJECTION = new String[] {
ContactsContract.RawContacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.PHOTO_URI,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Photo.CONTACT_ID };
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String filter = ""+ ContactsContract.Contacts.HAS_PHONE_NUMBER + " > 0 and " + Phone.TYPE +"=" + Phone.TYPE_MOBILE;
String order = ContactsContract.Contacts.DISPLAY_NAME + " ASC";// LIMIT " + limit + " offset " + lastId + "";
Cursor phoneCur = cr.query(uri, PROJECTION, filter, null, order);
while(phoneCur.moveToNext()) {
ContactDTO dto = new ContactDTO();
dto.setName("" + phoneCur.getString(phoneCur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)));
dto.setMobileNo("" + phoneCur.getString(phoneCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
dto.setPhotoUrl("" + phoneCur.getString(phoneCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.PHOTO_URI)));
dto.setContactId("" + phoneCur.getString(phoneCur.getColumnIndex(ContactsContract.CommonDataKinds.Photo.CONTACT_ID)));
contactList.add(dto);
}
phoneCur.close();
return contactList;
}
where ContactDTO is Simple POJO class.
I think the issue is because of the contact list sync with different
account.
Yes, your contact list sync many account. You should filter contact with Account type : ContactsContract.CommonDataKinds.Phone.ACCOUNT_TYPE_AND_DATA_SET.
Account you can research in :
https://developer.android.com/reference/android/accounts/AccountManager.html
You can search for the aggregated Contact instead of all RawContacts. This will give you only 1 contact with the given name (like in the Contacts app).
Example (changing your code):
private static final String[] PROJECTION = new String[] {
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY, // Honeycomb+ should use this
ContactsContract.CommonDataKinds.Phone.NUMBER
};
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(
ContactsContract.Contacts.CONTENT_URI,
PROJECTION,
null,
null,
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " COLLATE NOCASE ASC");
if (cursor != null) {
try {
int nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY);
String nameContact = cursor.getString(nameIndex);
finally {
cursor.close();
}
}
Source: https://developer.android.com/reference/android/provider/ContactsContract.Contacts.html

limiting number of rows in a ContentResolver.query() function

Is there a way to limit the number of returned rows to a cursor?
I have a phone with about 4000 contacts, I just need some of them.
this is the code i'm using
db = new dBHelper(this);
ContentResolver cr = getContentResolver();
Cursor cursor;
cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,null, null, null, ContactName + " ASC");
Log.i(TAG, CLASSNAME + " got contacts entries");
for (int it = 0; it <100 ; it++){//cursor.getCount()
Log.i(TAG, CLASSNAME + " getting string");
String mytimes_contacted = cursor.getString(cursor.getColumnIndex(dBHelper.times_contacted));
Log.i(TAG, CLASSNAME + " done from the string");
}
the Log i'm getting is
I/Check(11506): [ContactsPicker] got contacts entries
I/Check(11506): [ContactsPicker] getting first string
D/AndroidRuntime(11506): Shutting down VM
W/dalvikvm(11506): threadid=1: thread exiting with uncaught exception (group=0x2aac8578)
D/dalvikvm(11541): GC_CONCURRENT freed 923K, 46% free 4000K/7303K, external 1685K/2133K, paused 1ms+8ms
E/AndroidRuntime(11506): FATAL EXCEPTION: main
E/AndroidRuntime(11506): java.lang.RuntimeException: Unable to start activity ComponentInfo{~~my package name~~}: android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 3537
To limit the number of results in your cursor try:
cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,null, null, null, ContactName + " LIMIT 100");
while(cursor.moveToNext()) {
// something clever
}
From Android 11, that above solution will not work, you can try this one to fetch the data.
/**
* Call to fetch all media on device, it but be called synchronously since function is called on a background thread
*/
private fun fetchGalleryImages(
context: Context,
offset: Int,
limit: Int
): List<MediaItem> {
val galleryImageUrls = mutableListOf<MediaItem>()
try {
if (EasyPermissions.hasPermissions(
context,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
) {
// Define the columns that will be fetched
val projection = arrayOf(
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.DATE_ADDED,
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.MIME_TYPE,
MediaStore.Files.FileColumns.TITLE,
MediaStore.Video.Media.DURATION
)
val selection =
"${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?"
val selectionArgs = arrayOf(
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(),
MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString()
)
/**
* Change the way to fetch Media Store
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Get All data in Cursor by sorting in DESC order
context.contentResolver.query(
contentUri(),
projection,
Bundle().apply {
// Limit & Offset
putInt(ContentResolver.QUERY_ARG_LIMIT, limit)
putInt(ContentResolver.QUERY_ARG_OFFSET, offset)
// Sort function
putStringArray( // <-- This should be an array. I spent a whole day trying to figure out what I was doing wrong
ContentResolver.QUERY_ARG_SORT_COLUMNS,
arrayOf(MediaStore.Files.FileColumns.DATE_MODIFIED)
)
putInt(
ContentResolver.QUERY_ARG_SORT_DIRECTION,
ContentResolver.QUERY_SORT_DIRECTION_DESCENDING
)
// Selection
putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection)
putStringArray(
ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS,
selectionArgs
)
}, null
)
} else {
val sortOrder =
"${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC LIMIT $limit OFFSET $offset"
// Get All data in Cursor by sorting in DESC order
context.contentResolver.query(
contentUri(),
projection,
selection,
selectionArgs,
sortOrder
)
}?.use { cursor ->
while (cursor.moveToNext()) {
galleryImageUrls.add(
MediaItem(
cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID)),
ContentUris.withAppendedId(
contentUri(),
cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID))
),
cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA)),
cursor.getStringOrNull(cursor.getColumnIndex(MediaStore.Files.FileColumns.MIME_TYPE)),
cursor.getLongOrNull(cursor.getColumnIndex(MediaStore.Video.Media.DURATION))
)
)
}
}
}
} catch (ex: Exception) {
ex.printStackTrace()
}
return galleryImageUrls
}
The accepted answer is not valid anymore for android 11. In android 11 a constraint was added to not allow using LIMIT in sort value. You need to use the query with bundle parameters. For instance:
val bundle = Bundle().apply {
putInt(ContentResolver.QUERY_ARG_LIMIT, 100)
}
resolver.query(
ContactsContract.Contacts.CONTENT_URI,
projection,
bundle,
null
)
In android 26 query method is upgraded. This function is using these arguments.
Uri uri, String[] projection, Bundle queryArgs, CancellationSignal cancellationSignal
Below example I'm getting recent 5 pictures.
val whereArgs = arrayOf("image/jpeg", "image/png", "image/jpg")
val projection = arrayOf(MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE)
val selection =
"${MediaStore.Files.FileColumns.MIME_TYPE} = ? OR ${MediaStore.Files.FileColumns.MIME_TYPE} = ? OR ${MediaStore.Files.FileColumns.MIME_TYPE} = ?"
val queryArgs = Bundle()
val sortArgs = arrayOf(MediaStore.Images.ImageColumns.DATE_TAKEN)
queryArgs.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, sortArgs)
queryArgs.putInt(ContentResolver.QUERY_ARG_SORT_DIRECTION, ContentResolver.QUERY_SORT_DIRECTION_DESCENDING)
queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5)
queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection)
queryArgs.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, whereArgs)
val cursor = context!!.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
queryArgs,
null)
if (cursor!!.moveToFirst()) {
do {
val imageLocation = cursor.getString(1)
val imageFile = File(imageLocation)
if (imageFile.exists()) {
//access you file from imageLocation
}
} while (cursor.moveToNext())
fiveRecentlyImagesAdapter!!.notifyDataSetChanged()
}
If anyone is looking for Java version of the above Ignacio Tomas Crespo's answer ,
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
.buildUpon()
.encodedQuery("limit=" + offSet + "," + "100")
.build(),
columns,
null,
null,
null);
} else {
Bundle bundle = new Bundle();
bundle.putInt(ContentResolver.QUERY_ARG_LIMIT, 100);
cursor = context.getContentResolver()
.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
columns,
bundle,
null);
}

Categories

Resources