Fetch contacts ignoring accents - android

I fetch contacts from the phone as below. I would like to ignore accent for exemple the name "Jérome" would be return either for search "jero" or "jéro".
var contacts = listOf<Contact>()
val displayNameProjection = arrayOf(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)
val whereName = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?"
val whereNameParams = arrayOf( "%" + search + "%")
val contactCursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI,
displayNameProjection, whereName, whereNameParams,ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)
contactCursor?.let { cursor ->
contacts = generateSequence { if (cursor.moveToNext()) cursor else null }
.take(10)
.map {
Contact( it.getString(it.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)),
it.getString(it.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI)))
}
.toList()
cursor.close()
}

Use the CONTENT_FILTER_URI API to quickly search contacts by name, this should handle accents automatically for you:
String name = "jero";
Uri searchUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(name));
Cursor cur = getContentResolver().query(searchUri, null, null, null, null);
DatabaseUtils.dumpCursor(cur);
if (cur != null) cur.close();

Related

Querying for all files above a certain size with mediastore

I'm trying to query for every file in an android device using the mediastore API. Is there a shorter way of writing the selection string to include all the files in the device without having to write each for the Audio,Video,Images and NON_MEDIA? Also,how can I make the query,in addition,to get only the files of above a particular file size.
ContentResolver cr = context.getContentResolver();
Uri uri = MediaStore.Files.getContentUri("external");
// every column, although that is huge waste, you probably need
// BaseColumns.DATA (the path) only.
String[] projection = null;
// exclude media files, they would be here also.
String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_NONE + "OR" + MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
+ " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO + " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO;
String[] selectionArgs = null; // there is no ? in selection so null here
String sortOrder = null; // unordered
Cursor allFiles = cr.query(uri, projection, selection, selectionArgs, sortOrder);
Is there a shorter way of writing the selection string to include all
the files in the device without having to write each for the
Audio,Video,Images and NON_MEDIA?
yes, MediaStore.Files
This is how I achieved (below code is in kotlin)
private suspend fun getAllFiles(context : Context) : List<AllFiles>{
return withContext(Dispatchers.IO){
val fileCollection = mutableListOf<AllFiles>()
val contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
val projections = arrayOf(MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DISPLAY_NAME,
MediaStore.Files.FileColumns.MIME_TYPE)
context.contentResolver.query(contentUri,projections,null,null,null).use {
cursor->
if(cursor!=null && cursor.count > 0) {
val idIndex = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)
val displayNameIndex = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)
val typeIndex = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MIME_TYPE)
fileCollection.clear()
while (cursor.moveToNext()){
val id = cursor.getLong(idIndex)
val displayName = cursor.getString(displayNameIndex)
val type = cursor.getString(typeIndex)
fileCollection.add(AllFiles(id,displayName,type))
}
}
else if(cursor==null)
Log.d("myTag","cursor is nUll")
else
Log.d("myTag","Cursor count is ${cursor.count}")
}
fileCollection
}
}
To run above method on android 12 , we need
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
outside of onCreate
val intent = Intent(ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
Uri.parse("package:" + BuildConfig.APPLICATION_ID))
val storagePermissionResultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()
) {
if (Environment.isExternalStorageManager()) {
// Permission granted.
getData()
}
}
and inside onCreate
if(!Environment.isExternalStorageManager())
storagePermissionResultLauncher.launch(intent)
else
getData()
where getData() is
fun getData(){
val textview = binding.textviewFirst
lifecycleScope.launchWhenResumed {
textview.append(getAllFiles(requireContext()).toString())
}
}
and AllFile is
data class AllFiles(
val fileId : Long,
val displayName : String?,
val type : String?)
you can then filter returned list on type

List all music files in all android devices with where, limit and offset clauses

I am trying to implement a recyclerview which will show all the music files in my android device with a 10 per page pagination and contains a search text. I am using content resolver for this. My application is targeting till android 12. So my existing code is like:
fun getAllMusicFilesInDevice(offset: Int, searchText: String){
val tempAudioList: MutableList<Album> = ArrayList()
val uri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
else
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(
MediaStore.Audio.AudioColumns.DATA,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.AudioColumns.ALBUM,
MediaStore.Audio.ArtistColumns.ARTIST,
)
val selection = MediaStore.Audio.Media.DISPLAY_NAME + " LIKE ?"
val selectionArgs = arrayOf(searchText)
val sortOrder =
"${MediaStore.Audio.Media.DISPLAY_NAME} ASC LIMIT $PAGE_SIZE OFFSET $offset"
val c = mContext.contentResolver.query(
uri,
projection,
selection,
selectionArgs,
sortOrder
)
if (c != null) {
while (c.moveToNext()) {
try {
val path: String = c.getString(0)
val songName = c.getString(1)
val album: String = c.getString(2)
val artist: String = c.getString(3)
val extension = File(path).extension
if (isMusicFile(extension)) {
isMusicPresentInPlaylist(path, playlistDb) {
val audioModel =
Album(
name = songName,
path = path,
artist = artist,
album = album
)
tempAudioList.add(audioModel)
}
}
} catch (ex: Exception) {
}
}
c.close()
}}
This code does not give back any result and makes the app behave like its stuck. Please help me out on the mistakes I am making and anything I am doing wrong out here so that I get this function working.
UPDATE CURSOR CODE
val c = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val limit = Bundle().apply {
// Limit & Offset
putInt(ContentResolver.QUERY_ARG_LIMIT, AUDIO_PAGE_SIZE)
putInt(ContentResolver.QUERY_ARG_OFFSET, offset)
}
mContext.contentResolver.query(uri, projection, limit, null)
} else
mContext.contentResolver.query(
uri,
projection,
null,
null,
"${MediaStore.Audio.Media.DISPLAY_NAME} ASC LIMIT $AUDIO_PAGE_SIZE OFFSET $offset"
)
Note: this code is running in a background thread.

Android contentResolver query with MediaStore returns empty cursor

I'm trying to get all the images on the device, for now my code is:
val imageProjection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.DATE_TAKEN,
)
val cursor = context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
imageProjection,
null,
null,
"${MediaStore.Images.Media.DATE_MODIFIED} DESC"
)
cursor?.use {
val idColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
val nameColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
val sizeColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)
val dateColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN)
while (it.moveToNext()) {
val id = it.getLong(idColumn)
val name = it.getString(nameColumn)
val size = it.getString(sizeColumn)
val date = it.getString(dateColumn)
Log.d("IMAGE", "$id $name $size $date")
}
}
The query does give me a Cursor, but the Cursor appears to be empty as the code never enter the while.
I've checked that i have the READ_EXTERNAL_STORAGE permission, and i do have it, but it still doesn't work, i also tried using MediaStore.Images.Media.INTERNAL_CONTENT_URI as the query uri, but that didn't change anything, the result is always an empty cursor.
Can someone give me an hint on what i'm doing wrong?

Contact book read time on Android

I have written the following query to read contacts from a device.
private fun getContactPhoneNumbers(resolver: ContentResolver): Map<String, NameAndPhoneList> {
val startTime = System.nanoTime()
val map = hashMapOf<String, NameAndPhoneList>()
val projection = arrayOf(
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
ContactsContract.Contacts.PHOTO_THUMBNAIL_URI
)
val selection =
"${ContactsContract.Contacts.DISPLAY_NAME} NOT LIKE '' and ${ContactsContract.Contacts.DISPLAY_NAME} NOT NULL"
resolver.query(
ContactsContract.Contacts.CONTENT_URI,
projection,
selection,
null,
null,
null
)?.let { cursor ->
if (cursor.count > 0) {
while (cursor.moveToNext()) {
val id: String? =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
val name: String? =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
val photoUri =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI))
val phone =
if (cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0) {
val pCur: Cursor = resolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
arrayOf(id),
null
)!!
val numbers = mutableListOf<String>()
while (pCur.moveToNext()) {
val phoneNo: String = pCur.getString(
pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER
)
)
numbers.add(phoneNo)
}
pCur.close()
numbers
} else
null
// take contacts which either have email or phone numbers
if (id != null && name != null) {
map[id] = NameAndPhoneList(name, phone, photoUri?.let { Uri.parse(it) })
}
}
}
cursor.close()
}
val endTime = System.nanoTime() - startTime
Timber.i("$CONTACT_SYNC_PHONE_MAP_QUERY_TIME = $endTime")
return map
}
private data class NameAndPhoneList(
val name: String,
val phoneList: List<String>?,
val imageUri: Uri?
)
this is taking 112877872699 ns (~2 min) for a phonebook of length 6,300 contacts. Is this expected or can we optimize further?
for 6,300 contacts (assuming all have a phone) you're making 6,301 queries, which is why it's so slow...
Instead you can benefit from ContactsContract's "implicit join" feature, which allows you to get Contacts.CONTENT_URI fields when querying over the Data.CONTENT_URI table.
So, just query directly over the Data table, get all the phones including the CONTACT_ID, DISPLAY_NAME, etc. fields, and put it in some map contact-id => data.
Here's sample code in Java that can help - https://stackoverflow.com/a/44383937/819355

MediaStore.Files.FileColumns.DATA has been deprecated, what to use instead?

I am creating an app in which I list the recent images and videos from the user's gallery and display them on a RecyclerView using the file path. The code below is working on Android 7, but Android Studio is warning that MediaStore.Files.FileColumns.DATA has been deprecated. What to use instead?
// Get relevant columns for use later.
val projection = arrayOf(
MediaStore.Files.FileColumns.DATA
)
// Return only video and image metadata.
val selection = (MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
+ " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO)
val queryUri: Uri = MediaStore.Files.getContentUri("external")
val cursorLoader = CursorLoader(
requireContext(),
queryUri,
projection,
selection,
null,
MediaStore.Files.FileColumns.DATE_ADDED + " DESC" // Sort order.
)
val cursor: Cursor? = cursorLoader.loadInBackground()
if (cursor!!.moveToFirst()) {
do {
val columnIndex = cursor.getColumnIndex(projection[0])
val filePath = cursor.getString(columnIndex)
val mimeType = URLConnection.guessContentTypeFromName(filePath)
if (mimeType != null && mimeType.startsWith("image")) {
mediaList.add(GalleryMedia(filePath, "image"))
} else if (mimeType != null && mimeType.startsWith("video")) {
mediaList.add(GalleryMedia(filePath, "video"))
}
} while (cursor.moveToNext())
}
cursor.close()

Categories

Resources