diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d621cc5..569884ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========== +Version 5.1.2 *(2020-05-13)* +---------------------------- + + * Improved the handling of multiple SIM cards at once + * Added a Mark as Read action button in incoming message notifications + * Allow saving unknown numbers from the main screen easily + Version 5.1.1 *(2020-05-08)* ---------------------------- diff --git a/app/build.gradle b/app/build.gradle index 8490e2a1..46335361 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { applicationId "com.simplemobiletools.smsmessenger" minSdkVersion 22 targetSdkVersion 29 - versionCode 4 - versionName "5.1.1" + versionCode 5 + versionName "5.1.2" setProperty("archivesBaseName", "sms-messenger") } @@ -56,7 +56,7 @@ android { } dependencies { - implementation 'com.simplemobiletools:commons:5.27.24' + implementation 'com.simplemobiletools:commons:5.27.29' implementation 'org.greenrobot:eventbus:3.2.0' implementation 'com.klinkerapps:android-smsmms:5.2.6' implementation 'com.github.tibbi:IndicatorFastScroll:08f512858a' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c2f17766..770b80db 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -154,6 +154,15 @@ android:exported="true" android:taskAffinity="${applicationId}.SMS_SENT" /> + + + + + + 1) { availableSIMs.forEachIndexed { index, subscriptionInfo -> var label = subscriptionInfo.displayName.toString() @@ -370,16 +370,24 @@ class ThreadActivity : SimpleActivity() { showSelectedContacts() } + @SuppressLint("MissingPermission") private fun getThreadItems(): ArrayList { messages.sortBy { it.date } + val subscriptionIdToSimId = HashMap() + subscriptionIdToSimId[-1] = "?" + SubscriptionManager.from(this).activeSubscriptionInfoList?.forEachIndexed { index, subscriptionInfo -> + subscriptionIdToSimId[subscriptionInfo.subscriptionId] = "${index + 1}" + } + val items = ArrayList() var prevDateTime = 0 var hadUnreadItems = false messages.forEach { // do not show the date/time above every message, only if the difference between the 2 messages is at least MIN_DATE_TIME_DIFF_SECS if (it.date - prevDateTime > MIN_DATE_TIME_DIFF_SECS) { - items.add(ThreadDateTime(it.date)) + val simCardID = subscriptionIdToSimId[it.subscriptionId] ?: "?" + items.add(ThreadDateTime(it.date, simCardID)) prevDateTime = it.date } items.add(it) 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 2677c10f..ed427e89 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ConversationsAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ConversationsAdapter.kt @@ -1,5 +1,6 @@ package com.simplemobiletools.smsmessenger.adapters +import android.content.Intent import android.graphics.Typeface import android.view.Menu import android.view.View @@ -9,6 +10,8 @@ import com.bumptech.glide.Glide import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.extensions.formatDateOrTime +import com.simplemobiletools.commons.extensions.toast +import com.simplemobiletools.commons.helpers.KEY_PHONE import com.simplemobiletools.commons.helpers.SimpleContactsHelper import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.commons.views.FastScroller @@ -29,7 +32,11 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis override fun getActionMenuId() = R.menu.cab_conversations - override fun prepareActionMode(menu: Menu) {} + override fun prepareActionMode(menu: Menu) { + menu.apply { + findItem(R.id.cab_add_number_to_contact).isVisible = isOneItemSelected() && getSelectedItems().firstOrNull()?.isGroupConversation == false + } + } override fun actionItemPressed(id: Int) { if (selectedKeys.isEmpty()) { @@ -37,6 +44,7 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis } when (id) { + R.id.cab_add_number_to_contact -> addNumberToContact() R.id.cab_select_all -> selectAll() R.id.cab_delete -> askConfirmDelete() } @@ -105,6 +113,23 @@ class ConversationsAdapter(activity: SimpleActivity, var conversations: ArrayLis } } + private fun addNumberToContact() { + val conversation = getSelectedItems().firstOrNull() ?: return + Intent().apply { + action = Intent.ACTION_INSERT_OR_EDIT + type = "vnd.android.cursor.item/contact" + putExtra(KEY_PHONE, conversation.phoneNumber) + + if (resolveActivity(activity.packageManager) != null) { + activity.startActivity(this) + } else { + activity.toast(R.string.no_app_found) + } + } + } + + private fun getSelectedItems() = conversations.filter { selectedKeys.contains(it.id) } as ArrayList + override fun onViewRecycled(holder: ViewHolder) { super.onViewRecycled(holder) if (!activity.isDestroyed && !activity.isFinishing) { diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt index 0a609b19..0f5a6531 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt @@ -1,8 +1,10 @@ package com.simplemobiletools.smsmessenger.adapters +import android.annotation.SuppressLint import android.content.Intent import android.graphics.drawable.Drawable import android.net.Uri +import android.telephony.SubscriptionManager import android.view.Menu import android.view.View import android.view.ViewGroup @@ -42,6 +44,8 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) { private val roundedCornersRadius = resources.getDimension(R.dimen.normal_margin).toInt() + @SuppressLint("MissingPermission") + private val hasMultipleSIMCards = SubscriptionManager.from(activity).activeSubscriptionInfoList?.size ?: 0 > 1 init { setupDragListener(true) @@ -283,6 +287,14 @@ class ThreadAdapter(activity: SimpleActivity, var messages: ArrayList { Sms.ADDRESS, Sms.DATE, Sms.READ, - Sms.THREAD_ID + Sms.THREAD_ID, + Sms.SUBSCRIPTION_ID ) val selection = "${Sms.THREAD_ID} = ?" @@ -49,7 +50,7 @@ fun Context.getMessages(threadId: Int): ArrayList { val blockStatus = HashMap() var messages = ArrayList() queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor -> - val senderNumber = cursor.getStringValue(Sms.ADDRESS) + val senderNumber = cursor.getStringValue(Sms.ADDRESS) ?: return@queryCursor val isNumberBlocked = if (blockStatus.containsKey(senderNumber)) { blockStatus[senderNumber]!! @@ -72,9 +73,10 @@ fun Context.getMessages(threadId: Int): ArrayList { val date = (cursor.getLongValue(Sms.DATE) / 1000).toInt() val read = cursor.getIntValue(Sms.READ) == 1 val thread = cursor.getIntValue(Sms.THREAD_ID) + val subscriptionId = cursor.getIntValue(Sms.SUBSCRIPTION_ID) val participant = SimpleContact(0, 0, senderName, photoUri, senderNumber) val isMMS = false - val message = Message(id, body, type, arrayListOf(participant), date, read, thread, isMMS, null, senderName, photoUri) + val message = Message(id, body, type, arrayListOf(participant), date, read, thread, isMMS, null, senderName, photoUri, subscriptionId) messages.add(message) } @@ -93,7 +95,8 @@ fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList< Mms.DATE, Mms.READ, Mms.MESSAGE_BOX, - Mms.THREAD_ID + Mms.THREAD_ID, + Mms.SUBSCRIPTION_ID ) val selection = if (threadId == null) { @@ -117,6 +120,7 @@ fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList< val date = cursor.getLongValue(Mms.DATE).toInt() val read = cursor.getIntValue(Mms.READ) == 1 val threadId = cursor.getIntValue(Mms.THREAD_ID) + val subscriptionId = cursor.getIntValue(Mms.SUBSCRIPTION_ID) val participants = if (threadParticipants.containsKey(threadId)) { threadParticipants[threadId]!! } else { @@ -140,7 +144,7 @@ fun Context.getMMS(threadId: Int? = null, sortOrder: String? = null): ArrayList< } } - val message = Message(mmsId, body, type, participants, date, read, threadId, isMMS, attachment, senderName, senderPhotoUri) + val message = Message(mmsId, body, type, participants, date, read, threadId, isMMS, attachment, senderName, senderPhotoUri, subscriptionId) messages.add(message) participants.forEach { @@ -206,7 +210,7 @@ fun Context.getConversations(): ArrayList { val title = TextUtils.join(", ", names.toTypedArray()) val photoUri = if (phoneNumbers.size == 1) SimpleContactsHelper(this).getPhotoUriFromPhoneNumber(phoneNumbers.first()) else "" val isGroupConversation = phoneNumbers.size > 1 - val conversation = Conversation(id, snippet, date.toInt(), read, title, photoUri, isGroupConversation) + val conversation = Conversation(id, snippet, date.toInt(), read, title, photoUri, isGroupConversation, phoneNumbers.first()) conversations.add(conversation) } @@ -371,7 +375,7 @@ fun Context.getSuggestedContacts(): ArrayList { val sortOrder = "${Sms.DATE} DESC LIMIT 20" queryCursor(uri, projection, selection, selectionArgs, sortOrder, showErrors = true) { cursor -> - val senderNumber = cursor.getStringValue(Sms.ADDRESS) + val senderNumber = cursor.getStringValue(Sms.ADDRESS) ?: return@queryCursor val namePhoto = getNameAndPhotoFromPhoneNumber(senderNumber) if (namePhoto == null || namePhoto.name == senderNumber || isNumberBlocked(senderNumber)) { return@queryCursor @@ -415,7 +419,7 @@ fun Context.getNameAndPhotoFromPhoneNumber(number: String): NamePhoto? { return NamePhoto(number, null) } -fun Context.insertNewSMS(address: String, subject: String, body: String, date: Long, read: Int, threadId: Long, type: Int) { +fun Context.insertNewSMS(address: String, subject: String, body: String, date: Long, read: Int, threadId: Long, type: Int, subscriptionId: Int): Int { val uri = Sms.CONTENT_URI val contentValues = ContentValues().apply { put(Sms.ADDRESS, address) @@ -425,9 +429,11 @@ fun Context.insertNewSMS(address: String, subject: String, body: String, date: L put(Sms.READ, read) put(Sms.THREAD_ID, threadId) put(Sms.TYPE, type) + put(Sms.SUBSCRIPTION_ID, subscriptionId) } - contentResolver.insert(uri, contentValues) + val newUri = contentResolver.insert(uri, contentValues) + return newUri?.lastPathSegment?.toInt() ?: 0 } fun Context.deleteConversation(id: Int) { @@ -487,7 +493,7 @@ fun Context.isNumberBlocked(number: String): Boolean { } @SuppressLint("NewApi") -fun Context.showReceivedMessageNotification(address: String, body: String, threadID: Int, bitmap: Bitmap? = null) { +fun Context.showReceivedMessageNotification(address: String, body: String, threadID: Int, bitmap: Bitmap?, messageId: Int, isMMS: Boolean) { val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) val channelId = "simple_sms_messenger" @@ -517,6 +523,13 @@ fun Context.showReceivedMessageNotification(address: String, body: String, threa val summaryText = getString(R.string.new_message) val sender = getNameAndPhotoFromPhoneNumber(address)?.name ?: "" + val markAsReadIntent = Intent(this, MarkAsReadReceiver::class.java).apply { + action = MARK_AS_READ + putExtra(MESSAGE_ID, messageId) + putExtra(MESSAGE_IS_MMS, isMMS) + } + val markAsReadPendingIntent = PendingIntent.getBroadcast(this, 0, markAsReadIntent, PendingIntent.FLAG_CANCEL_CURRENT) + val largeIcon = bitmap ?: SimpleContactsHelper(this).getContactLetterIcon(sender) val builder = NotificationCompat.Builder(this, channelId) .setContentTitle(sender) @@ -530,7 +543,8 @@ fun Context.showReceivedMessageNotification(address: String, body: String, threa .setCategory(Notification.CATEGORY_MESSAGE) .setAutoCancel(true) .setSound(soundUri, AudioManager.STREAM_NOTIFICATION) + .addAction(R.drawable.ic_check_vector, getString(R.string.mark_as_read), markAsReadPendingIntent) .setChannelId(channelId) - notificationManager.notify(threadID, builder.build()) + notificationManager.notify(messageId, builder.build()) } 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 33fb77fd..8620bfa8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt @@ -11,6 +11,11 @@ const val THREAD_ATTACHMENT_URI = "thread_attachment_uri" const val THREAD_ATTACHMENT_URIS = "thread_attachment_uris" const val USE_SIM_ID_PREFIX = "use_sim_id_" +private const val PATH = "com.simplemobiletools.smsmessenger.action." +const val MARK_AS_READ = PATH + "mark_as_read" +const val MESSAGE_ID = "message_id" +const val MESSAGE_IS_MMS = "message_is_mms" + // view types for the thread list view const val THREAD_DATE_TIME = 1 const val THREAD_RECEIVED_MESSAGE = 2 diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Conversation.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Conversation.kt index 81420432..65329fc8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Conversation.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Conversation.kt @@ -2,5 +2,4 @@ package com.simplemobiletools.smsmessenger.models data class Conversation( val id: Int, val snippet: String, val date: Int, val read: Boolean, val title: String, val photoUri: String, - val isGroupConversation: Boolean -) + val isGroupConversation: Boolean, val phoneNumber: String) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Message.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Message.kt index 2b6f24c2..4285d527 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Message.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/Message.kt @@ -5,6 +5,6 @@ import com.simplemobiletools.commons.models.SimpleContact data class Message( val id: Int, val body: String, val type: Int, val participants: ArrayList, val date: Int, val read: Boolean, val thread: Int, - val isMMS: Boolean, val attachment: MessageAttachment?, val senderName: String, val senderPhotoUri: String) : ThreadItem() { + val isMMS: Boolean, val attachment: MessageAttachment?, val senderName: String, val senderPhotoUri: String, val subscriptionId: Int) : ThreadItem() { fun isReceivedMessage() = type == Telephony.Sms.MESSAGE_TYPE_INBOX } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ThreadDateTime.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ThreadDateTime.kt index 7b2d9293..d69d81fa 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ThreadDateTime.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/models/ThreadDateTime.kt @@ -1,3 +1,3 @@ package com.simplemobiletools.smsmessenger.models -open class ThreadDateTime(val date: Int) : ThreadItem() +open class ThreadDateTime(val date: Int, val simID: String) : ThreadItem() diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MarkAsReadReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MarkAsReadReceiver.kt new file mode 100644 index 00000000..f0cdff85 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MarkAsReadReceiver.kt @@ -0,0 +1,23 @@ +package com.simplemobiletools.smsmessenger.receivers + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.simplemobiletools.commons.extensions.notificationManager +import com.simplemobiletools.smsmessenger.extensions.markMessageRead +import com.simplemobiletools.smsmessenger.helpers.MARK_AS_READ +import com.simplemobiletools.smsmessenger.helpers.MESSAGE_ID +import com.simplemobiletools.smsmessenger.helpers.MESSAGE_IS_MMS + +class MarkAsReadReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + when (intent.action) { + MARK_AS_READ -> { + val messageId = intent.getIntExtra(MESSAGE_ID, 0) + val isMMS = intent.getBooleanExtra(MESSAGE_IS_MMS, false) + context.markMessageRead(messageId, isMMS) + context.notificationManager.cancel(messageId) + } + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MmsReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MmsReceiver.kt index a35b51f5..865fb5a3 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MmsReceiver.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/MmsReceiver.kt @@ -31,7 +31,7 @@ class MmsReceiver : com.klinker.android.send_message.MmsReceivedReceiver() { null } - context.showReceivedMessageNotification(address, mms.body, mms.thread, glideBitmap) + context.showReceivedMessageNotification(address, mms.body, mms.thread, glideBitmap, mms.id, true) } } 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 3649bc9d..17c6f62b 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/SmsReceiver.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/SmsReceiver.kt @@ -20,6 +20,7 @@ class SmsReceiver : BroadcastReceiver() { var threadId = 0L val type = Telephony.Sms.MESSAGE_TYPE_INBOX val read = 0 + val subscriptionId = intent.getIntExtra("subscription", -1) messages.forEach { address = it.originatingAddress ?: "" @@ -30,8 +31,8 @@ class SmsReceiver : BroadcastReceiver() { } if (!context.isNumberBlocked(address)) { - context.insertNewSMS(address, subject, body, date, read, threadId, type) - context.showReceivedMessageNotification(address, body, threadId.toInt()) + val messageId = context.insertNewSMS(address, subject, body, date, read, threadId, type, subscriptionId) + context.showReceivedMessageNotification(address, body, threadId.toInt(), null, messageId, false) refreshMessages() } } diff --git a/app/src/main/res/layout/item_thread_date_time.xml b/app/src/main/res/layout/item_thread_date_time.xml index 5f93edcf..413c3ef8 100644 --- a/app/src/main/res/layout/item_thread_date_time.xml +++ b/app/src/main/res/layout/item_thread_date_time.xml @@ -1,10 +1,40 @@ - + android:layout_marginTop="@dimen/medium_margin"> + + + + + + + + diff --git a/app/src/main/res/menu/cab_conversations.xml b/app/src/main/res/menu/cab_conversations.xml index 2a2792e0..54c3891e 100644 --- a/app/src/main/res/menu/cab_conversations.xml +++ b/app/src/main/res/menu/cab_conversations.xml @@ -1,6 +1,11 @@ + - + diff --git a/fastlane/metadata/android/en-US/changelogs/5.txt b/fastlane/metadata/android/en-US/changelogs/5.txt new file mode 100644 index 00000000..196c855e --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/5.txt @@ -0,0 +1,3 @@ + * Improved the handling of multiple SIM cards at once + * Added a Mark as Read action button in incoming message notifications + * Allow saving unknown numbers from the main screen easily