diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt index 5c8303be..8b99d1e4 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/MainActivity.kt @@ -187,6 +187,7 @@ class MainActivity : SimpleActivity() { binding.mainMenu.getToolbar().menu.apply { findItem(R.id.more_apps_from_us).isVisible = !resources.getBoolean(com.simplemobiletools.commons.R.bool.hide_google_relations) findItem(R.id.show_recycle_bin).isVisible = config.useRecycleBin + findItem(R.id.show_archived).isVisible = config.isArchiveAvailable } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt index 71505fee..40b7d207 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt @@ -249,11 +249,12 @@ class ThreadActivity : SimpleActivity() { private fun refreshMenuItems() { val firstPhoneNumber = participants.firstOrNull()?.phoneNumbers?.firstOrNull()?.value + val archiveAvailable = config.isArchiveAvailable binding.threadToolbar.menu.apply { findItem(R.id.delete).isVisible = threadItems.isNotEmpty() findItem(R.id.restore).isVisible = threadItems.isNotEmpty() && isRecycleBin - findItem(R.id.archive).isVisible = threadItems.isNotEmpty() && conversation?.isArchived == false && !isRecycleBin - findItem(R.id.unarchive).isVisible = threadItems.isNotEmpty() && conversation?.isArchived == true && !isRecycleBin + findItem(R.id.archive).isVisible = threadItems.isNotEmpty() && conversation?.isArchived == false && !isRecycleBin && archiveAvailable + findItem(R.id.unarchive).isVisible = threadItems.isNotEmpty() && conversation?.isArchived == true && !isRecycleBin && archiveAvailable findItem(R.id.rename_conversation).isVisible = participants.size > 1 && conversation != null && !isRecycleBin findItem(R.id.conversation_details).isVisible = conversation != null && !isRecycleBin findItem(R.id.block_number).title = addLockedLabelIfNeeded(com.simplemobiletools.commons.R.string.block_number) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ConversationsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ConversationsAdapter.kt index 75129ea3..8a4995c1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ConversationsAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ConversationsAdapter.kt @@ -28,6 +28,7 @@ class ConversationsAdapter( val isSingleSelection = isOneItemSelected() val selectedConversation = selectedItems.firstOrNull() ?: return val isGroupConversation = selectedConversation.isGroupConversation + val archiveAvailable = activity.config.isArchiveAvailable menu.apply { findItem(R.id.cab_block_number).title = activity.addLockedLabelIfNeeded(com.simplemobiletools.commons.R.string.block_number) @@ -38,6 +39,7 @@ class ConversationsAdapter( findItem(R.id.cab_rename_conversation).isVisible = isSingleSelection && isGroupConversation findItem(R.id.cab_mark_as_read).isVisible = selectedItems.any { !it.read } findItem(R.id.cab_mark_as_unread).isVisible = selectedItems.any { it.read } + findItem(R.id.cab_archive).isVisible = archiveAvailable checkPinBtnVisibility(this) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt index 39ff6210..b213314e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt @@ -6,6 +6,7 @@ import android.content.ContentResolver import android.content.ContentValues import android.content.Context import android.database.Cursor +import android.database.sqlite.SQLiteException import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri @@ -262,16 +263,21 @@ fun Context.getMMSSender(msgId: Long): String { } fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList = ArrayList()): ArrayList { + val archiveAvailable = config.isArchiveAvailable + val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true") - val projection = arrayOf( + val projection = mutableListOf( Threads._ID, Threads.SNIPPET, Threads.DATE, Threads.READ, Threads.RECIPIENT_IDS, - Threads.ARCHIVED ) + if (archiveAvailable) { + projection += Threads.ARCHIVED + } + var selection = "${Threads.MESSAGE_COUNT} > ?" var selectionArgs = arrayOf("0") if (threadId != null) { @@ -284,39 +290,68 @@ fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList< val conversations = ArrayList() val simpleContactHelper = SimpleContactsHelper(this) val blockedNumbers = getBlockedNumbers() - queryCursor(uri, projection, selection, selectionArgs, sortOrder, true) { cursor -> - val id = cursor.getLongValue(Threads._ID) - var snippet = cursor.getStringValue(Threads.SNIPPET) ?: "" - if (snippet.isEmpty()) { - snippet = getThreadSnippet(id) - } + try { + queryCursorUnsafe(uri, projection.toTypedArray(), selection, selectionArgs, sortOrder) { cursor -> + val id = cursor.getLongValue(Threads._ID) + var snippet = cursor.getStringValue(Threads.SNIPPET) ?: "" + if (snippet.isEmpty()) { + snippet = getThreadSnippet(id) + } - var date = cursor.getLongValue(Threads.DATE) - if (date.toString().length > 10) { - date /= 1000 - } + var date = cursor.getLongValue(Threads.DATE) + if (date.toString().length > 10) { + date /= 1000 + } - val rawIds = cursor.getStringValue(Threads.RECIPIENT_IDS) - val recipientIds = rawIds.split(" ").filter { it.areDigitsOnly() }.map { it.toInt() }.toMutableList() - val phoneNumbers = getThreadPhoneNumbers(recipientIds) - if (phoneNumbers.isEmpty() || phoneNumbers.any { isNumberBlocked(it, blockedNumbers) }) { - return@queryCursor - } + val rawIds = cursor.getStringValue(Threads.RECIPIENT_IDS) + val recipientIds = rawIds.split(" ").filter { it.areDigitsOnly() }.map { it.toInt() }.toMutableList() + val phoneNumbers = getThreadPhoneNumbers(recipientIds) + if (phoneNumbers.isEmpty() || phoneNumbers.any { isNumberBlocked(it, blockedNumbers) }) { + return@queryCursorUnsafe + } - val names = getThreadContactNames(phoneNumbers, privateContacts) - val title = TextUtils.join(", ", names.toTypedArray()) - val photoUri = if (phoneNumbers.size == 1) simpleContactHelper.getPhotoUriFromPhoneNumber(phoneNumbers.first()) else "" - val isGroupConversation = phoneNumbers.size > 1 - val read = cursor.getIntValue(Threads.READ) == 1 - val archived = cursor.getIntValue(Threads.ARCHIVED) == 1 - val conversation = Conversation(id, snippet, date.toInt(), read, title, photoUri, isGroupConversation, phoneNumbers.first(), isArchived = archived) - conversations.add(conversation) + val names = getThreadContactNames(phoneNumbers, privateContacts) + val title = TextUtils.join(", ", names.toTypedArray()) + val photoUri = if (phoneNumbers.size == 1) simpleContactHelper.getPhotoUriFromPhoneNumber(phoneNumbers.first()) else "" + val isGroupConversation = phoneNumbers.size > 1 + val read = cursor.getIntValue(Threads.READ) == 1 + val archived = if (archiveAvailable) cursor.getIntValue(Threads.ARCHIVED) == 1 else false + val conversation = Conversation(id, snippet, date.toInt(), read, title, photoUri, isGroupConversation, phoneNumbers.first(), isArchived = archived) + conversations.add(conversation) + } + } catch (sqliteException: SQLiteException) { + if (sqliteException.message?.contains("no such column: archived") == true && archiveAvailable) { + config.isArchiveAvailable = false + return getConversations(threadId, privateContacts) + } else { + showErrorToast(sqliteException) + } + } catch (e: Exception) { + showErrorToast(e) } conversations.sortByDescending { it.date } return conversations } +private fun Context.queryCursorUnsafe( + uri: Uri, + projection: Array, + selection: String? = null, + selectionArgs: Array? = null, + sortOrder: String? = null, + callback: (cursor: Cursor) -> Unit +) { + val cursor = contentResolver.query(uri, projection, selection, selectionArgs, sortOrder) + cursor?.use { + if (cursor.moveToFirst()) { + do { + callback(cursor) + } while (cursor.moveToNext()) + } + } +} + fun Context.getConversationIds(): List { val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true") val projection = arrayOf(Threads._ID) @@ -713,7 +748,16 @@ fun Context.updateConversationArchivedStatus(threadId: Long, archived: Boolean) } val selection = "${Threads._ID} = ?" val selectionArgs = arrayOf(threadId.toString()) - contentResolver.update(uri, values, selection, selectionArgs) + try { + contentResolver.update(uri, values, selection, selectionArgs) + } catch (sqliteException: SQLiteException) { + if (sqliteException.message?.contains("no such column: archived") == true && config.isArchiveAvailable) { + config.isArchiveAvailable = false + return + } else { + throw sqliteException + } + } if (archived) { conversationsDB.moveToArchive(threadId) } else { diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt index 88eb740a..56fc4a10 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Config.kt @@ -111,4 +111,8 @@ class Config(context: Context) : BaseConfig(context) { var lastRecycleBinCheck: Long get() = prefs.getLong(LAST_RECYCLE_BIN_CHECK, 0L) set(lastRecycleBinCheck) = prefs.edit().putLong(LAST_RECYCLE_BIN_CHECK, lastRecycleBinCheck).apply() + + var isArchiveAvailable: Boolean + get() = prefs.getBoolean(IS_ARCHIVE_AVAILABLE, true) + set(isArchiveAvailable) = prefs.edit().putBoolean(IS_ARCHIVE_AVAILABLE, isArchiveAvailable).apply() } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt index 0127ac34..ef1ae601 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt @@ -43,6 +43,7 @@ const val MESSAGE_ID = "message_id" const val USE_RECYCLE_BIN = "use_recycle_bin" const val LAST_RECYCLE_BIN_CHECK = "last_recycle_bin_check" const val IS_RECYCLE_BIN = "is_recycle_bin" +const val IS_ARCHIVE_AVAILABLE = "is_archive_available" private const val PATH = "com.simplemobiletools.smsmessenger.action." const val MARK_AS_READ = PATH + "mark_as_read" diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/SmsReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/SmsReceiver.kt index 13af6ce0..3544adeb 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/SmsReceiver.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/SmsReceiver.kt @@ -113,7 +113,9 @@ class SmsReceiver : BroadcastReceiver() { subscriptionId ) context.messagesDB.insertOrUpdate(message) - context.updateConversationArchivedStatus(threadId, false) + if (context.config.isArchiveAvailable) { + context.updateConversationArchivedStatus(threadId, false) + } refreshMessages() context.showReceivedMessageNotification(newMessageId, address, body, threadId, bitmap) }