diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 27942e83..28a469e1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,11 @@ + + + + + diff --git a/app/src/main/java/com/ismartcoding/plain/features/BaseContentHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/BaseContentHelper.kt index 735f8f1a..652613bf 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/BaseContentHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/BaseContentHelper.kt @@ -145,8 +145,9 @@ abstract class BaseContentHelper { val cursor = getSearchCursor(context, query) val ids = mutableSetOf() if (cursor?.moveToFirst() == true) { + val cache = mutableMapOf() do { - ids.add(cursor.getStringValue(idKey)) + ids.add(cursor.getStringValue(idKey, cache)) } while (cursor.moveToNext()) } @@ -212,9 +213,10 @@ abstract class BaseContentHelper { ) if (cursor != null) { cursor.moveToFirst() + val cache = mutableMapOf() while (!cursor.isAfterLast) { - val id = cursor.getStringValue(MediaStore.MediaColumns._ID) - val path = cursor.getStringValue(MediaStore.MediaColumns.DATA) + val id = cursor.getStringValue(MediaStore.MediaColumns._ID, cache) + val path = cursor.getStringValue(MediaStore.MediaColumns.DATA, cache) paths.add(path) try { // File.delete can throw a security exception val f = File(path) diff --git a/app/src/main/java/com/ismartcoding/plain/features/Permissions.kt b/app/src/main/java/com/ismartcoding/plain/features/Permissions.kt index 4cad1116..9fd4335f 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/Permissions.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/Permissions.kt @@ -42,6 +42,9 @@ enum class Permission { CAMERA, SYSTEM_ALERT_WINDOW, RECORD_AUDIO, + READ_MEDIA_IMAGES, + READ_MEDIA_VIDEOS, + READ_MEDIA_AUDIO, NONE; fun getText(): String { @@ -236,6 +239,9 @@ object Permissions { Permission.SEND_SMS, Permission.POST_NOTIFICATIONS, Permission.RECORD_AUDIO, + Permission.READ_MEDIA_IMAGES, + Permission.READ_MEDIA_VIDEOS, + Permission.READ_MEDIA_AUDIO, ).forEach { permission -> map[permission] = activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) { canContinue = true diff --git a/app/src/main/java/com/ismartcoding/plain/features/audio/AudioHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/audio/AudioHelper.kt index 0cadd8b4..b6f67f10 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/audio/AudioHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/audio/AudioHelper.kt @@ -64,14 +64,15 @@ object AudioHelper : BaseContentHelper() { val cursor = getSearchCursor(context, query, limit, offset, sortBy.toSortBy()) val result = mutableListOf() if (cursor?.moveToFirst() == true) { + val cache = mutableMapOf() do { - val id = cursor.getStringValue(MediaStore.Audio.Media._ID) - val title = cursor.getStringValue(MediaStore.Audio.Media.TITLE) - val artist = cursor.getStringValue(MediaStore.Audio.Media.ARTIST).replace(MediaStore.UNKNOWN_STRING, "") - val size = cursor.getLongValue(MediaStore.Audio.Media.SIZE) - val duration = cursor.getLongValue(MediaStore.Audio.Media.DURATION) / 1000 - val path = cursor.getStringValue(MediaStore.Audio.Media.DATA) - val bucketId = cursor.getStringValue(MediaStore.Audio.Media.BUCKET_ID) + val id = cursor.getStringValue(MediaStore.Audio.Media._ID, cache) + val title = cursor.getStringValue(MediaStore.Audio.Media.TITLE, cache) + val artist = cursor.getStringValue(MediaStore.Audio.Media.ARTIST, cache).replace(MediaStore.UNKNOWN_STRING, "") + val size = cursor.getLongValue(MediaStore.Audio.Media.SIZE, cache) + val duration = cursor.getLongValue(MediaStore.Audio.Media.DURATION, cache) / 1000 + val path = cursor.getStringValue(MediaStore.Audio.Media.DATA, cache) + val bucketId = cursor.getStringValue(MediaStore.Audio.Media.BUCKET_ID, cache) result.add(DAudio(id, title, artist, path, duration, size, bucketId)) } while (cursor.moveToNext()) } @@ -82,10 +83,11 @@ object AudioHelper : BaseContentHelper() { val cursor = getSearchCursor(context, query) val result = mutableListOf() if (cursor?.moveToFirst() == true) { + val cache = mutableMapOf() do { - val id = cursor.getStringValue(MediaStore.Audio.Media._ID) - val title = cursor.getStringValue(MediaStore.Audio.Media.TITLE) - val size = cursor.getLongValue(MediaStore.Audio.Media.SIZE) + val id = cursor.getStringValue(MediaStore.Audio.Media._ID, cache) + val title = cursor.getStringValue(MediaStore.Audio.Media.TITLE, cache) + val size = cursor.getLongValue(MediaStore.Audio.Media.SIZE, cache) result.add(TagRelationStub(id, title,size)) } while (cursor.moveToNext()) } @@ -111,10 +113,11 @@ object AudioHelper : BaseContentHelper() { ) cursor?.use { c -> + val cache = mutableMapOf() while (c.moveToNext()) { - val bucketId = c.getStringValue(MediaStore.Audio.Media.BUCKET_ID) - val bucketName = c.getStringValue(MediaStore.Audio.Media.BUCKET_DISPLAY_NAME) - val path = c.getStringValue(MediaStore.Audio.Media.DATA) + val bucketId = c.getStringValue(MediaStore.Audio.Media.BUCKET_ID, cache) + val bucketName = c.getStringValue(MediaStore.Audio.Media.BUCKET_DISPLAY_NAME, cache) + val path = c.getStringValue(MediaStore.Audio.Media.DATA, cache) val bucket = bucketMap[bucketName] if (bucket != null) { if (bucket.topItems.size < 4) { diff --git a/app/src/main/java/com/ismartcoding/plain/features/call/BlockedNumberHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/call/BlockedNumberHelper.kt index d3835b31..455be800 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/call/BlockedNumberHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/call/BlockedNumberHelper.kt @@ -18,10 +18,10 @@ object BlockedNumberHelper { BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER ) - MainApp.instance.queryCursor(uri, projection) { cursor -> - val id = cursor.getStringValue(BlockedNumberContract.BlockedNumbers.COLUMN_ID) - val number = cursor.getStringValue(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER) - val normalizedNumber = cursor.getStringValue(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER) + MainApp.instance.queryCursor(uri, projection) { cursor, cache -> + val id = cursor.getStringValue(BlockedNumberContract.BlockedNumbers.COLUMN_ID, cache) + val number = cursor.getStringValue(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER, cache) + val normalizedNumber = cursor.getStringValue(BlockedNumberContract.BlockedNumbers.COLUMN_E164_NUMBER, cache) val comparableNumber = normalizedNumber.trimToComparableNumber() blockedNumbers.add(BlockedNumber(id, number, normalizedNumber, comparableNumber)) } diff --git a/app/src/main/java/com/ismartcoding/plain/features/call/CallHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/call/CallHelper.kt index 23aeb0f2..2ccde499 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/call/CallHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/call/CallHelper.kt @@ -94,15 +94,16 @@ object CallHelper : BaseContentHelper() { val cursor = getSearchCursor(context, query, limit, offset, SortBy(CallLog.Calls._ID, SortDirection.DESC)) val items = mutableListOf() if (cursor?.moveToFirst() == true) { + val cache = mutableMapOf() do { - val id = cursor.getStringValue(CallLog.Calls._ID) - val number = cursor.getStringValue(CallLog.Calls.NUMBER) - val name = cursor.getStringValue(CallLog.Calls.CACHED_NAME) - val photoUri = cursor.getStringValue(CallLog.Calls.CACHED_PHOTO_URI) - val startTS = cursor.getTimeValue(CallLog.Calls.DATE) - val duration = cursor.getIntValue(CallLog.Calls.DURATION) - val type = cursor.getIntValue(CallLog.Calls.TYPE) - val accountId = cursor.getStringValue(CallLog.Calls.PHONE_ACCOUNT_ID) + val id = cursor.getStringValue(CallLog.Calls._ID, cache) + val number = cursor.getStringValue(CallLog.Calls.NUMBER, cache) + val name = cursor.getStringValue(CallLog.Calls.CACHED_NAME, cache) + val photoUri = cursor.getStringValue(CallLog.Calls.CACHED_PHOTO_URI, cache) + val startTS = cursor.getTimeValue(CallLog.Calls.DATE, cache) + val duration = cursor.getIntValue(CallLog.Calls.DURATION, cache) + val type = cursor.getIntValue(CallLog.Calls.TYPE, cache) + val accountId = cursor.getStringValue(CallLog.Calls.PHONE_ACCOUNT_ID, cache) items.add(DCall(id, number, name, photoUri, startTS, duration, type, accountId)) } while (cursor.moveToNext()) } diff --git a/app/src/main/java/com/ismartcoding/plain/features/contact/ContactHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/contact/ContactHelper.kt index c3e497de..560038af 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/contact/ContactHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/contact/ContactHelper.kt @@ -225,20 +225,21 @@ object ContactHelper : BaseContentHelper() { val cursor = getSearchCursorWithSortOrder(context, query, limit, offset, SortBy(ContactsContract.Data.DISPLAY_NAME_PRIMARY, SortDirection.ASC)) val items = mutableListOf() if (cursor?.moveToFirst() == true) { + val cache = mutableMapOf() do { - val accountName = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) - val rawId = cursor.getStringValue(ContactsContract.Data.RAW_CONTACT_ID) - val prefix = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX) - val givenName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME) - val middleName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME) - val familyName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME) - val suffix = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX) - val photoUri = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.PHOTO_URI) - val starred = cursor.getIntValue(ContactsContract.CommonDataKinds.StructuredName.STARRED) - val contactId = cursor.getStringValue(ContactsContract.Data.CONTACT_ID) - val thumbnailUri = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI) - val ringtone = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.CUSTOM_RINGTONE) - val updatedAt = cursor.getTimeValue(ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP) + val accountName = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME, cache) + val rawId = cursor.getStringValue(ContactsContract.Data.RAW_CONTACT_ID, cache) + val prefix = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX, cache) + val givenName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, cache) + val middleName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, cache) + val familyName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, cache) + val suffix = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, cache) + val photoUri = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.PHOTO_URI, cache) + val starred = cursor.getIntValue(ContactsContract.CommonDataKinds.StructuredName.STARRED, cache) + val contactId = cursor.getStringValue(ContactsContract.Data.CONTACT_ID, cache) + val thumbnailUri = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI, cache) + val ringtone = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.CUSTOM_RINGTONE, cache) + val updatedAt = cursor.getTimeValue(ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP, cache) val nicknames = contentMap[rawId]?.nicknames ?: arrayListOf() val notes = contentMap[rawId]?.notes ?: arrayListOf() diff --git a/app/src/main/java/com/ismartcoding/plain/features/contact/ContentHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/contact/ContentHelper.kt index b28d3c3c..dd13a2d4 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/contact/ContentHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/contact/ContentHelper.kt @@ -43,65 +43,65 @@ object ContentHelper { ContactsContract.Data.DATA6, ) - context.queryCursor(uri, projection) { cursor -> - val id = cursor.getStringValue(ContactsContract.Data.RAW_CONTACT_ID) + context.queryCursor(uri, projection) { cursor, cache -> + val id = cursor.getStringValue(ContactsContract.Data.RAW_CONTACT_ID, cache) if (map[id] == null) { map[id] = Content() } - when (cursor.getStringValue(ContactsContract.Data.MIMETYPE)) { + when (cursor.getStringValue(ContactsContract.Data.MIMETYPE, cache)) { ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE -> { - val startDate = cursor.getStringValue(ContactsContract.CommonDataKinds.Event.START_DATE) - val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Event.TYPE) - val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Event.LABEL) + val startDate = cursor.getStringValue(ContactsContract.CommonDataKinds.Event.START_DATE, cache) + val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Event.TYPE, cache) + val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Event.LABEL, cache) map[id]?.events?.add(ContentItem(startDate, type, label)) } ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE -> { - val address = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS) - val type = cursor.getIntValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE) - val label = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredPostal.LABEL) + val address = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, cache) + val type = cursor.getIntValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, cache) + val label = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredPostal.LABEL, cache) map[id]?.addresses?.add(ContentItem(address, type, label)) } ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> { - val email = cursor.getStringValue(ContactsContract.CommonDataKinds.Email.DATA) - val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Email.TYPE) - val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Email.LABEL) + val email = cursor.getStringValue(ContactsContract.CommonDataKinds.Email.DATA, cache) + val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Email.TYPE, cache) + val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Email.LABEL, cache) map[id]?.emails?.add(ContentItem(email, type, label)) } ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> { - val number = cursor.getStringValue(ContactsContract.CommonDataKinds.Phone.NUMBER) - val normalizedNumber = cursor.getStringValue(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER) - val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Phone.TYPE) - val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Phone.LABEL) + val number = cursor.getStringValue(ContactsContract.CommonDataKinds.Phone.NUMBER, cache) + val normalizedNumber = cursor.getStringValue(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER, cache) + val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Phone.TYPE, cache) + val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Phone.LABEL, cache) map[id]?.phoneNumbers?.add(PhoneNumber(number, type, label, normalizedNumber)) } ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE -> { - val url = cursor.getStringValue(ContactsContract.CommonDataKinds.Website.URL) - val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Website.TYPE) - val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Website.LABEL) + val url = cursor.getStringValue(ContactsContract.CommonDataKinds.Website.URL, cache) + val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Website.TYPE, cache) + val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Website.LABEL, cache) map[id]?.websites?.add(ContentItem(url, type, label)) } ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE -> { - val name = cursor.getStringValue(ContactsContract.CommonDataKinds.Nickname.NAME) + val name = cursor.getStringValue(ContactsContract.CommonDataKinds.Nickname.NAME, cache) map[id]?.nicknames?.add(name) } ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE -> { - val value = cursor.getStringValue(ContactsContract.CommonDataKinds.Im.DATA) - val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Im.PROTOCOL) - val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL) + val value = cursor.getStringValue(ContactsContract.CommonDataKinds.Im.DATA, cache) + val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Im.PROTOCOL, cache) + val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, cache) map[id]?.ims?.add(ContentItem(value, type, label)) } ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE -> { - val note = cursor.getStringValue(ContactsContract.CommonDataKinds.Note.NOTE) + val note = cursor.getStringValue(ContactsContract.CommonDataKinds.Note.NOTE, cache) map[id]?.notes?.add(note) } ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE -> { - val company = cursor.getStringValue(ContactsContract.CommonDataKinds.Organization.COMPANY) - val title = cursor.getStringValue(ContactsContract.CommonDataKinds.Organization.TITLE) + val company = cursor.getStringValue(ContactsContract.CommonDataKinds.Organization.COMPANY, cache) + val title = cursor.getStringValue(ContactsContract.CommonDataKinds.Organization.TITLE, cache) map[id]?.organizations?.add(Organization(company, title)) } ContactsContract.CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE -> { - val groupId = cursor.getIntValue(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID) + val groupId = cursor.getIntValue(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID, cache) map[id]?.groupIds?.add(groupId) } } diff --git a/app/src/main/java/com/ismartcoding/plain/features/contact/GroupHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/contact/GroupHelper.kt index b9d83f8f..d4d212e8 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/contact/GroupHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/contact/GroupHelper.kt @@ -22,11 +22,11 @@ object GroupHelper { val selection = "${ContactsContract.Groups.AUTO_ADD} = ? AND ${ContactsContract.Groups.FAVORITES} = ?" val selectionArgs = arrayOf("0", "0") - context.queryCursor(uri, projection, selection, selectionArgs) { cursor -> - val id = cursor.getLongValue(ContactsContract.Groups._ID) - val title = cursor.getStringValue(ContactsContract.Groups.TITLE) + context.queryCursor(uri, projection, selection, selectionArgs) { cursor, cache -> + val id = cursor.getLongValue(ContactsContract.Groups._ID, cache) + val title = cursor.getStringValue(ContactsContract.Groups.TITLE, cache) - val systemId = cursor.getStringValue(ContactsContract.Groups.SYSTEM_ID) + val systemId = cursor.getStringValue(ContactsContract.Groups.SYSTEM_ID, cache) if (groups.map { it.name }.contains(title) && systemId.isNotEmpty()) { return@queryCursor } diff --git a/app/src/main/java/com/ismartcoding/plain/features/contact/SourceHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/contact/SourceHelper.kt index 8d1dbc18..65073ee0 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/contact/SourceHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/contact/SourceHelper.kt @@ -24,9 +24,9 @@ object SourceHelper { ContactsContract.RawContacts.ACCOUNT_NAME, ContactsContract.RawContacts.ACCOUNT_TYPE ) - ) { cursor -> - val name = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) - val type = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_TYPE) + ) { cursor, cache -> + val name = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME, cache) + val type = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_TYPE, cache) if (!sources.any { it.name == name && it.type == type }) { sources.add(DContactSource(name, type)) } diff --git a/app/src/main/java/com/ismartcoding/plain/features/file/FileHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/file/FileHelper.kt index ae2b4bbf..d321f67f 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/file/FileHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/file/FileHelper.kt @@ -45,13 +45,14 @@ object FileHelper : BaseContentHelper() { val cursor = getSearchCursor(context, query, limit, offset, sortBy.toSortBy()) val result = mutableListOf() if (cursor?.moveToFirst() == true) { + val cache = mutableMapOf() do { - val id = cursor.getStringValue(MediaStore.Files.FileColumns._ID) - val title = cursor.getStringValue(MediaStore.Files.FileColumns.TITLE) - val size = cursor.getLongValue(MediaStore.Files.FileColumns.SIZE) - val path = cursor.getStringValue(MediaStore.Files.FileColumns.DATA) - val updatedAt = cursor.getTimeValue(MediaStore.Files.FileColumns.DATE_MODIFIED) - val mediaType = cursor.getIntValue(MediaStore.Files.FileColumns.MEDIA_TYPE) + val id = cursor.getStringValue(MediaStore.Files.FileColumns._ID, cache) + val title = cursor.getStringValue(MediaStore.Files.FileColumns.TITLE, cache) + val size = cursor.getLongValue(MediaStore.Files.FileColumns.SIZE, cache) + val path = cursor.getStringValue(MediaStore.Files.FileColumns.DATA, cache) + val updatedAt = cursor.getTimeValue(MediaStore.Files.FileColumns.DATE_MODIFIED, cache) + val mediaType = cursor.getIntValue(MediaStore.Files.FileColumns.MEDIA_TYPE, cache) result.add(DFile(title, path, "", updatedAt, size, mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_NONE, 0)) } while (cursor.moveToNext()) diff --git a/app/src/main/java/com/ismartcoding/plain/features/file/FileSystemHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/file/FileSystemHelper.kt index 7a4988c6..6b2ae760 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/file/FileSystemHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/file/FileSystemHelper.kt @@ -11,6 +11,7 @@ import androidx.core.os.bundleOf import com.ismartcoding.lib.extensions.getDirectChildrenCount import com.ismartcoding.lib.extensions.getLongValue import com.ismartcoding.lib.extensions.getStringValue +import com.ismartcoding.lib.extensions.getTimeValue import com.ismartcoding.lib.isRPlus import com.ismartcoding.plain.R import com.ismartcoding.plain.features.locale.LocaleHelper.getString @@ -263,15 +264,16 @@ object FileSystemHelper { ) context.contentResolver?.query(uri, projection, queryArgs, null)?.use { cursor -> if (cursor.moveToFirst()) { + val cache = mutableMapOf() do { - val path = cursor.getStringValue(MediaStore.Files.FileColumns.DATA) + val path = cursor.getStringValue(MediaStore.Files.FileColumns.DATA, cache) if (File(path).isDirectory) { continue } - val name = cursor.getStringValue(MediaStore.Files.FileColumns.DISPLAY_NAME) - val size = cursor.getLongValue(MediaStore.Files.FileColumns.SIZE) - val updatedAt = Instant.fromEpochMilliseconds(cursor.getLongValue(MediaStore.Files.FileColumns.DATE_MODIFIED) * 1000L) + val name = cursor.getStringValue(MediaStore.Files.FileColumns.DISPLAY_NAME, cache) + val size = cursor.getLongValue(MediaStore.Files.FileColumns.SIZE, cache) + val updatedAt = Instant.fromEpochMilliseconds(cursor.getLongValue(MediaStore.Files.FileColumns.DATE_MODIFIED, cache) * 1000L) items.add(DFile(name, path, "", updatedAt, size, false, 0)) } while (cursor.moveToNext()) } diff --git a/app/src/main/java/com/ismartcoding/plain/features/image/ImageHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/image/ImageHelper.kt index 0103cc33..03a33ad2 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/image/ImageHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/image/ImageHelper.kt @@ -50,12 +50,13 @@ object ImageHelper : BaseContentHelper() { val cursor = getSearchCursor(context, query, limit, offset, sortBy.toSortBy()) val result = mutableListOf() if (cursor?.moveToFirst() == true) { + val cache = mutableMapOf() do { - val id = cursor.getStringValue(MediaStore.Images.Media._ID) - val title = cursor.getStringValue(MediaStore.Images.Media.TITLE) - val size = cursor.getLongValue(MediaStore.Images.Media.SIZE) - val path = cursor.getStringValue(MediaStore.Images.Media.DATA) - val bucketId = cursor.getStringValue(MediaStore.Images.Media.BUCKET_ID) + val id = cursor.getStringValue(MediaStore.Images.Media._ID, cache) + val title = cursor.getStringValue(MediaStore.Images.Media.TITLE, cache) + val size = cursor.getLongValue(MediaStore.Images.Media.SIZE, cache) + val path = cursor.getStringValue(MediaStore.Images.Media.DATA, cache) + val bucketId = cursor.getStringValue(MediaStore.Images.Media.BUCKET_ID, cache) result.add(DImage(id, title, path, size, bucketId)) } while (cursor.moveToNext()) } @@ -66,10 +67,11 @@ object ImageHelper : BaseContentHelper() { val cursor = getSearchCursor(context, query) val result = mutableListOf() if (cursor?.moveToFirst() == true) { + val cache = mutableMapOf() do { - val id = cursor.getStringValue(MediaStore.Images.Media._ID) - val title = cursor.getStringValue(MediaStore.Images.Media.TITLE) - val size = cursor.getLongValue(MediaStore.Images.Media.SIZE) + val id = cursor.getStringValue(MediaStore.Images.Media._ID, cache) + val title = cursor.getStringValue(MediaStore.Images.Media.TITLE, cache) + val size = cursor.getLongValue(MediaStore.Images.Media.SIZE, cache) result.add(TagRelationStub(id, title,size)) } while (cursor.moveToNext()) } @@ -96,10 +98,11 @@ object ImageHelper : BaseContentHelper() { ) cursor?.use { c -> + val cache = mutableMapOf() while (c.moveToNext()) { - val bucketId = c.getStringValue(MediaStore.Images.Media.BUCKET_ID) - val bucketName = c.getStringValue(MediaStore.Images.Media.BUCKET_DISPLAY_NAME) - val path = c.getStringValue(MediaStore.Images.Media.DATA) + val bucketId = c.getStringValue(MediaStore.Images.Media.BUCKET_ID, cache) + val bucketName = c.getStringValue(MediaStore.Images.Media.BUCKET_DISPLAY_NAME, cache) + val path = c.getStringValue(MediaStore.Images.Media.DATA, cache) val bucket = bucketMap[bucketId] if (bucket != null) { if (bucket.topItems.size < 4) { diff --git a/app/src/main/java/com/ismartcoding/plain/features/sms/SmsHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/sms/SmsHelper.kt index 05938ae4..52cd27d2 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/sms/SmsHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/sms/SmsHelper.kt @@ -94,17 +94,18 @@ object SmsHelper : BaseContentHelper() { val cursor = getSearchCursorWithSortOrder(context, query, limit, offset, SortBy(Telephony.Sms.DATE, SortDirection.DESC)) val items = mutableListOf() if (cursor?.moveToFirst() == true) { + val cache = mutableMapOf() do { items.add( DMessage( - cursor.getStringValue(Telephony.Sms._ID), - cursor.getStringValue(Telephony.Sms.BODY), - cursor.getStringValue(Telephony.Sms.ADDRESS), - cursor.getTimeValue(Telephony.Sms.DATE), - cursor.getStringValue(Telephony.Sms.SERVICE_CENTER), - cursor.getIntValue(Telephony.Sms.READ) == 1, - cursor.getStringValue(Telephony.Sms.THREAD_ID), - cursor.getIntValue(Telephony.Sms.TYPE) + cursor.getStringValue(Telephony.Sms._ID, cache), + cursor.getStringValue(Telephony.Sms.BODY, cache), + cursor.getStringValue(Telephony.Sms.ADDRESS, cache), + cursor.getTimeValue(Telephony.Sms.DATE, cache), + cursor.getStringValue(Telephony.Sms.SERVICE_CENTER, cache), + cursor.getIntValue(Telephony.Sms.READ, cache) == 1, + cursor.getStringValue(Telephony.Sms.THREAD_ID, cache), + cursor.getIntValue(Telephony.Sms.TYPE, cache) ) ) } while (cursor.moveToNext()) diff --git a/app/src/main/java/com/ismartcoding/plain/features/video/VideoHelper.kt b/app/src/main/java/com/ismartcoding/plain/features/video/VideoHelper.kt index f782a752..1d385210 100644 --- a/app/src/main/java/com/ismartcoding/plain/features/video/VideoHelper.kt +++ b/app/src/main/java/com/ismartcoding/plain/features/video/VideoHelper.kt @@ -52,13 +52,14 @@ object VideoHelper : BaseContentHelper() { val cursor = getSearchCursor(context, query, limit, offset, sortBy.toSortBy()) val result = mutableListOf() cursor?.use { c -> + val cache = mutableMapOf() while (c.moveToNext()) { - val id = cursor.getStringValue(MediaStore.Video.Media._ID) - val title = cursor.getStringValue(MediaStore.Video.Media.TITLE) - val size = cursor.getLongValue(MediaStore.Video.Media.SIZE) - val duration = cursor.getLongValue(MediaStore.Video.Media.DURATION) / 1000 - val path = cursor.getStringValue(MediaStore.Video.Media.DATA) - val bucketId = cursor.getStringValue(MediaStore.Video.Media.BUCKET_ID) + val id = cursor.getStringValue(MediaStore.Video.Media._ID, cache) + val title = cursor.getStringValue(MediaStore.Video.Media.TITLE, cache) + val size = cursor.getLongValue(MediaStore.Video.Media.SIZE, cache) + val duration = cursor.getLongValue(MediaStore.Video.Media.DURATION, cache) / 1000 + val path = cursor.getStringValue(MediaStore.Video.Media.DATA, cache) + val bucketId = cursor.getStringValue(MediaStore.Video.Media.BUCKET_ID, cache) result.add(DVideo(id, title, path, duration, size, bucketId)) } } @@ -69,10 +70,11 @@ object VideoHelper : BaseContentHelper() { val cursor = getSearchCursor(context, query) val result = mutableListOf() if (cursor?.moveToFirst() == true) { + val cache = mutableMapOf() do { - val id = cursor.getStringValue(MediaStore.Video.Media._ID) - val title = cursor.getStringValue(MediaStore.Video.Media.TITLE) - val size = cursor.getLongValue(MediaStore.Video.Media.SIZE) + val id = cursor.getStringValue(MediaStore.Video.Media._ID, cache) + val title = cursor.getStringValue(MediaStore.Video.Media.TITLE, cache) + val size = cursor.getLongValue(MediaStore.Video.Media.SIZE, cache) result.add(TagRelationStub(id, title,size)) } while (cursor.moveToNext()) } @@ -98,10 +100,11 @@ object VideoHelper : BaseContentHelper() { ) cursor?.use { c -> + val cache = mutableMapOf() while (c.moveToNext()) { - val bucketId = c.getStringValue(MediaStore.Video.Media.BUCKET_ID) - val bucketName = c.getStringValue(MediaStore.Video.Media.BUCKET_DISPLAY_NAME) - val path = c.getStringValue(MediaStore.Video.Media.DATA) + val bucketId = c.getStringValue(MediaStore.Video.Media.BUCKET_ID, cache) + val bucketName = c.getStringValue(MediaStore.Video.Media.BUCKET_DISPLAY_NAME, cache) + val path = c.getStringValue(MediaStore.Video.Media.DATA, cache) val bucket = bucketMap[bucketId] if (bucket != null) { if (bucket.topItems.size < 4) { diff --git a/app/src/main/java/com/ismartcoding/plain/ui/feed/FeedEntriesDialog.kt b/app/src/main/java/com/ismartcoding/plain/ui/feed/FeedEntriesDialog.kt index 7b55bdb8..fb09adbe 100644 --- a/app/src/main/java/com/ismartcoding/plain/ui/feed/FeedEntriesDialog.kt +++ b/app/src/main/java/com/ismartcoding/plain/ui/feed/FeedEntriesDialog.kt @@ -88,7 +88,8 @@ class FeedEntriesDialog : BaseListDrawerDialog() { } contentResolver.query(event.uri, null, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { - val fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME) + val cache = mutableMapOf() + val fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME, cache) DialogHelper.showConfirmDialog(requireContext(), "", LocaleHelper.getStringF(R.string.exported_to, "name", fileName)) } } diff --git a/app/src/main/java/com/ismartcoding/plain/ui/models/BackupRestoreViewModel.kt b/app/src/main/java/com/ismartcoding/plain/ui/models/BackupRestoreViewModel.kt index 2ef64ead..62bcae89 100644 --- a/app/src/main/java/com/ismartcoding/plain/ui/models/BackupRestoreViewModel.kt +++ b/app/src/main/java/com/ismartcoding/plain/ui/models/BackupRestoreViewModel.kt @@ -45,7 +45,8 @@ class BackupRestoreViewModel : ViewModel() { } contentResolver.query(uri, null, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { - val fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME) + val cache = mutableMapOf() + val fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME, cache) DialogHelper.hideLoading() coMain { DialogHelper.showConfirmDialog(context, "", LocaleHelper.getStringF(R.string.exported_to, "name", fileName)) @@ -64,7 +65,8 @@ class BackupRestoreViewModel : ViewModel() { DialogHelper.showLoading() contentResolver.query(uri, null, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { - val fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME) + val cache = mutableMapOf() + val fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME, cache) if (!fileName.endsWith(".zip")) { DialogHelper.showMessage(R.string.invalid_file) DialogHelper.hideLoading() diff --git a/app/src/main/java/com/ismartcoding/plain/ui/page/ChatPage.kt b/app/src/main/java/com/ismartcoding/plain/ui/page/ChatPage.kt index b308f04d..9e4d4bca 100644 --- a/app/src/main/java/com/ismartcoding/plain/ui/page/ChatPage.kt +++ b/app/src/main/java/com/ismartcoding/plain/ui/page/ChatPage.kt @@ -139,12 +139,13 @@ fun ChatPage( } val items = mutableListOf() withIO { + val cache = mutableMapOf() event.uris.forEach { uri -> context.contentResolver.query(uri, null, null, null, null) ?.use { cursor -> try { cursor.moveToFirst() - var fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME) + var fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME, cache) if (event.type == PickFileType.IMAGE_VIDEO) { val mimeType = context.contentResolver.getType(uri) val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) ?: "" @@ -152,7 +153,7 @@ fun ChatPage( fileName = fileName.getFilenameWithoutExtension() + "." + extension } } - val size = cursor.getLongValue(OpenableColumns.SIZE) + val size = cursor.getLongValue(OpenableColumns.SIZE, cache) cursor.close() val dir = when { fileName.isVideoFast() -> { diff --git a/app/src/main/java/com/ismartcoding/plain/ui/views/texteditor/EditorInsertImageDialog.kt b/app/src/main/java/com/ismartcoding/plain/ui/views/texteditor/EditorInsertImageDialog.kt index f7e349ae..d124296c 100644 --- a/app/src/main/java/com/ismartcoding/plain/ui/views/texteditor/EditorInsertImageDialog.kt +++ b/app/src/main/java/com/ismartcoding/plain/ui/views/texteditor/EditorInsertImageDialog.kt @@ -32,7 +32,8 @@ class EditorInsertImageDialog : BaseBottomSheetDialog cursor.moveToFirst() - val fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME) + val cache = mutableMapOf() + val fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME, cache) cursor.close() try { val dir = Environment.DIRECTORY_PICTURES diff --git a/lib/src/main/java/com/ismartcoding/lib/extensions/Context.kt b/lib/src/main/java/com/ismartcoding/lib/extensions/Context.kt index 74b9dd92..51ae0042 100644 --- a/lib/src/main/java/com/ismartcoding/lib/extensions/Context.kt +++ b/lib/src/main/java/com/ismartcoding/lib/extensions/Context.kt @@ -72,12 +72,13 @@ fun Context.queryCursor( selection: String? = null, selectionArgs: Array? = null, sortOrder: String? = null, - callback: (cursor: Cursor) -> Unit + callback: (cursor: Cursor, indexCache: MutableMap) -> Unit ) { contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)?.use { cursor -> if (cursor.moveToFirst()) { + val cache = mutableMapOf() do { - callback(cursor) + callback(cursor, cache) } while (cursor.moveToNext()) } } @@ -206,7 +207,8 @@ fun Context.getMediaContent(path: String, baseUri: Uri): Uri? { val cursor = contentResolver.query(baseUri, projection, selection, selectionArgs, null) cursor?.use { if (cursor.moveToFirst()) { - val id = cursor.getStringValue(MediaStore.Images.Media._ID) + val cache = mutableMapOf() + val id = cursor.getStringValue(MediaStore.Images.Media._ID, cache) return Uri.withAppendedPath(baseUri, id) } } diff --git a/lib/src/main/java/com/ismartcoding/lib/extensions/Cursor.kt b/lib/src/main/java/com/ismartcoding/lib/extensions/Cursor.kt index 47c4b30e..9c0303ba 100644 --- a/lib/src/main/java/com/ismartcoding/lib/extensions/Cursor.kt +++ b/lib/src/main/java/com/ismartcoding/lib/extensions/Cursor.kt @@ -1,25 +1,25 @@ package com.ismartcoding.lib.extensions -import android.annotation.SuppressLint import android.database.Cursor import androidx.core.database.getIntOrNull import androidx.core.database.getLongOrNull import kotlinx.datetime.Instant -@SuppressLint("Range") -fun Cursor.getStringValue(key: String): String = getString(getColumnIndex(key)) ?: "" +// https://developer.android.com/training/data-storage/room/accessing-data#kotlin -@SuppressLint("Range") -fun Cursor.getStringValueOrNull(key: String): String? = getString(getColumnIndex(key)) +// Cache the column indices so that you don't need to call getColumnIndex() each time you process a row from the query result. +fun Cursor.getColumnIndex(key: String, cache: MutableMap): Int { + return cache.getOrElse(key) { + val index = getColumnIndex(key) + cache[key] = index + index + } +} -@SuppressLint("Range") -fun Cursor.getIntValue(key: String): Int = getIntOrNull(getColumnIndex(key)) ?: 0 +fun Cursor.getStringValue(key: String, cache: MutableMap): String = getString(getColumnIndex(key, cache)) ?: "" -fun Cursor.getIntValueOrNull(key: String): Int? = getIntOrNull(getColumnIndex(key)) +fun Cursor.getIntValue(key: String, cache: MutableMap): Int = getIntOrNull(getColumnIndex(key, cache)) ?: 0 -@SuppressLint("Range") -fun Cursor.getLongValue(key: String): Long = getLongOrNull(getColumnIndex(key)) ?: 0L +fun Cursor.getLongValue(key: String, cache: MutableMap): Long = getLongOrNull(getColumnIndex(key, cache)) ?: 0L -fun Cursor.getLongValueOrNull(key: String): Long? = getLongOrNull(getColumnIndex(key)) - -fun Cursor.getTimeValue(key: String): Instant = Instant.fromEpochMilliseconds(getLongValue(key)) \ No newline at end of file +fun Cursor.getTimeValue(key: String, cache: MutableMap): Instant = Instant.fromEpochMilliseconds(getLongValue(key, cache)) \ No newline at end of file diff --git a/lib/src/main/java/com/ismartcoding/lib/extensions/Uri.kt b/lib/src/main/java/com/ismartcoding/lib/extensions/Uri.kt index bc7b934b..2261403d 100644 --- a/lib/src/main/java/com/ismartcoding/lib/extensions/Uri.kt +++ b/lib/src/main/java/com/ismartcoding/lib/extensions/Uri.kt @@ -16,7 +16,8 @@ fun Uri.getFileName(context: Context): String { OpenableColumns.DISPLAY_NAME ), null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { - fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME) + val cache = mutableMapOf() + fileName = cursor.getStringValue(OpenableColumns.DISPLAY_NAME, cache) } } }