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)
}
}
}