package com.simplemobiletools.smsmessenger.extensions import android.annotation.SuppressLint import android.content.ContentValues import android.content.Context import android.database.Cursor import android.provider.ContactsContract import android.provider.ContactsContract.CommonDataKinds import android.provider.Telephony import android.text.TextUtils import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS import com.simplemobiletools.commons.helpers.isMarshmallowPlus import com.simplemobiletools.smsmessenger.helpers.Config import com.simplemobiletools.smsmessenger.models.Message val Context.config: Config get() = Config.newInstance(applicationContext) fun Context.getMessages(threadID: Int? = null): ArrayList { val messages = ArrayList() val hasContactsPermission = hasPermission(PERMISSION_READ_CONTACTS) val uri = Telephony.Sms.CONTENT_URI val projection = arrayOf( Telephony.Sms._ID, Telephony.Sms.SUBJECT, Telephony.Sms.BODY, Telephony.Sms.TYPE, Telephony.Sms.ADDRESS, Telephony.Sms.PERSON, Telephony.Sms.DATE, Telephony.Sms.READ, Telephony.Sms.THREAD_ID ) val selection = if (threadID == null) { "1 == 1) GROUP BY (${Telephony.Sms.THREAD_ID}" } else { "${Telephony.Sms.THREAD_ID} = ?" } val selectionArgs = if (threadID == null) { null } else { arrayOf(threadID.toString()) } var cursor: Cursor? = null try { cursor = contentResolver.query(uri, projection, selection, selectionArgs, null) if (cursor?.moveToFirst() == true) { do { val id = cursor.getIntValue(Telephony.Sms._ID) val subject = cursor.getStringValue(Telephony.Sms.SUBJECT) ?: "" val body = cursor.getStringValue(Telephony.Sms.BODY) val type = cursor.getIntValue(Telephony.Sms.TYPE) var senderName = cursor.getStringValue(Telephony.Sms.ADDRESS) val senderNumber = senderName val date = (cursor.getLongValue(Telephony.Sms.DATE) / 1000).toInt() val read = cursor.getIntValue(Telephony.Sms.READ) == 1 val person = cursor.getIntValue(Telephony.Sms.PERSON) val thread = cursor.getIntValue(Telephony.Sms.THREAD_ID) if (hasContactsPermission) { if (senderName != null && person != 0) { senderName = getPersonsName(person) ?: senderName } else if (senderName.areDigitsOnly()) { val contactId = getNameFromPhoneNumber(senderName) if (contactId != null) { senderName = getPersonsName(contactId) ?: senderName } } } val message = Message(id, subject, body, type, senderName, senderNumber, date, read, thread) messages.add(message) } while (cursor.moveToNext()) } } catch (e: Exception) { showErrorToast(e) } finally { cursor?.close() } return messages } fun Context.getPersonsName(id: Int): String? { val uri = ContactsContract.Data.CONTENT_URI val projection = arrayOf( CommonDataKinds.StructuredName.GIVEN_NAME, CommonDataKinds.StructuredName.MIDDLE_NAME, CommonDataKinds.StructuredName.FAMILY_NAME, CommonDataKinds.Organization.COMPANY, CommonDataKinds.Organization.TITLE, ContactsContract.Data.MIMETYPE ) val selection = "(${ContactsContract.Data.MIMETYPE} = ? OR ${ContactsContract.Data.MIMETYPE} = ?) AND ${ContactsContract.Data.CONTACT_ID} = ?" val selectionArgs = arrayOf( CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE, id.toString() ) var cursor: Cursor? = null try { cursor = contentResolver.query(uri, projection, selection, selectionArgs, null) if (cursor?.moveToFirst() == true) { do { val mimetype = cursor.getStringValue(ContactsContract.Data.MIMETYPE) val isPerson = mimetype == CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE if (isPerson) { val firstName = cursor.getStringValue(CommonDataKinds.StructuredName.GIVEN_NAME) ?: "" val middleName = cursor.getStringValue(CommonDataKinds.StructuredName.MIDDLE_NAME) ?: "" val familyName = cursor.getStringValue(CommonDataKinds.StructuredName.FAMILY_NAME) ?: "" if (firstName.isNotEmpty() || middleName.isNotEmpty() || familyName.isNotEmpty()) { val names = arrayOf(firstName, middleName, familyName).filter { it.isNotEmpty() } return TextUtils.join(" ", names) } } val isOrganization = mimetype == CommonDataKinds.Organization.CONTENT_ITEM_TYPE if (isOrganization) { val company = cursor.getStringValue(CommonDataKinds.Organization.COMPANY) ?: "" val jobTitle = cursor.getStringValue(CommonDataKinds.Organization.TITLE) ?: "" if (company.isNotEmpty() || jobTitle.isNotEmpty()) { return "$company $jobTitle".trim() } } } while (cursor.moveToNext()) } } catch (e: Exception) { showErrorToast(e) } finally { cursor?.close() } return null } fun Context.getNameFromPhoneNumber(number: String): Int? { val uri = CommonDataKinds.Phone.CONTENT_URI val projection = arrayOf( ContactsContract.Data.CONTACT_ID ) val selection = "${CommonDataKinds.Phone.NUMBER} = ? OR ${CommonDataKinds.Phone.NORMALIZED_NUMBER} = ?" val selectionArgs = arrayOf(number, number) var cursor: Cursor? = null try { cursor = contentResolver.query(uri, projection, selection, selectionArgs, null) if (cursor?.moveToFirst() == true) { return cursor.getIntValue(ContactsContract.Data.CONTACT_ID) } } catch (e: Exception) { showErrorToast(e) } finally { cursor?.close() } return null } fun Context.insertNewSMS(address: String, subject: String, body: String, date: Long, threadId: Long) { val uri = Telephony.Sms.CONTENT_URI val contentValues = ContentValues().apply { put(Telephony.Sms.ADDRESS, address) put(Telephony.Sms.SUBJECT, subject) put(Telephony.Sms.BODY, body) put(Telephony.Sms.DATE, date) put(Telephony.Sms.READ, 0) put(Telephony.Sms.THREAD_ID, threadId) put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_INBOX) } contentResolver.insert(uri, contentValues) } fun Context.deleteThread(id: Int) { val uri = Telephony.Sms.CONTENT_URI val selection = "${Telephony.Sms.THREAD_ID} = ?" val selectionArgs = arrayOf(id.toString()) contentResolver.delete(uri, selection, selectionArgs) } fun Context.deleteMessage(id: Int) { val uri = Telephony.Sms.CONTENT_URI val selection = "${Telephony.Sms._ID} = ?" val selectionArgs = arrayOf(id.toString()) contentResolver.delete(uri, selection, selectionArgs) } fun Context.markSMSRead(id: Int) { val uri = Telephony.Sms.CONTENT_URI val contentValues = ContentValues().apply { put(Telephony.Sms.READ, 1) } val selection = "${Telephony.Sms._ID} = ? AND ${Telephony.Sms.READ} = ?" val selectionArgs = arrayOf(id.toString(), "0") contentResolver.update(uri, contentValues, selection, selectionArgs) } @SuppressLint("NewApi") fun Context.getThreadId(address: String): Long { return if (isMarshmallowPlus()) { Telephony.Threads.getOrCreateThreadId(this, address) } else { 0 } }