diff --git a/build.gradle b/build.gradle index 90d78eaf8..e390ef559 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { propMinSdkVersion = 21 propTargetSdkVersion = propCompileSdkVersion propVersionCode = 1 - propVersionName = '5.31.10' + propVersionName = '5.31.11' kotlin_version = '1.4.10' } diff --git a/commons/build.gradle b/commons/build.gradle index 43b0a037a..42a73a76e 100644 --- a/commons/build.gradle +++ b/commons/build.gradle @@ -33,6 +33,7 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.0.1' implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' + implementation 'joda-time:joda-time:2.10.1' api 'androidx.appcompat:appcompat:1.2.0' api 'com.github.ajalt.reprint:core:3.3.0@aar' diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/String.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/String.kt index f42c61f0d..6ee0a21f4 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/String.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/String.kt @@ -11,10 +11,15 @@ import android.text.Spannable import android.text.SpannableString import android.text.TextUtils import android.text.style.ForegroundColorSpan +import android.widget.TextView import com.bumptech.glide.signature.ObjectKey import com.simplemobiletools.commons.helpers.* +import org.joda.time.DateTime +import org.joda.time.format.DateTimeFormat import java.io.File +import java.text.DateFormat import java.text.Normalizer +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -226,6 +231,31 @@ fun String.highlightTextFromNumbers(textToHighlight: String, adjustedPrimaryColo return spannableString } +fun String.getDateTimeFromDateString(viewToUpdate: TextView? = null): DateTime { + val dateFormats = getDateFormats() + var date = DateTime() + for (format in dateFormats) { + try { + date = DateTime.parse(this, DateTimeFormat.forPattern(format)) + + val formatter = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()) + var localPattern = (formatter as SimpleDateFormat).toLocalizedPattern() + + val hasYear = format.contains("y") + if (!hasYear) { + localPattern = localPattern.replace("y", "").replace(",", "").trim() + date = date.withYear(DateTime().year) + } + + val formattedString = date.toString(localPattern) + viewToUpdate?.text = formattedString + break + } catch (ignored: Exception) { + } + } + return date +} + fun String.getMimeType(): String { val typesMap = HashMap().apply { put("323", "text/h323") diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/helpers/MyContactsContentProvider.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/helpers/MyContactsContentProvider.kt index 4803da352..3bbf5a93e 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/helpers/MyContactsContentProvider.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/helpers/MyContactsContentProvider.kt @@ -9,7 +9,7 @@ import com.simplemobiletools.commons.extensions.getIntValue import com.simplemobiletools.commons.extensions.getStringValue import com.simplemobiletools.commons.models.SimpleContact -// used for sharing privately stored contacts in Simple Contacts with Simple Dialer and Simple SMS Messenger +// used for sharing privately stored contacts in Simple Contacts with Simple Dialer, Simple SMS Messenger and Simple Calendar Pro class MyContactsContentProvider { companion object { private const val AUTHORITY = "com.simplemobiletools.commons.contactsprovider" @@ -21,6 +21,8 @@ class MyContactsContentProvider { const val COL_NAME = "name" const val COL_PHOTO_URI = "photo_uri" const val COL_PHONE_NUMBERS = "phone_numbers" + const val COL_BIRTHDAYS = "birthdays" + const val COL_ANNIVERSARIES = "anniversaries" fun getSimpleContacts(context: Context, cursor: Cursor?): ArrayList { val contacts = ArrayList() @@ -38,10 +40,15 @@ class MyContactsContentProvider { val name = cursor.getStringValue(COL_NAME) val photoUri = cursor.getStringValue(COL_PHOTO_URI) val phoneNumbersJson = cursor.getStringValue(COL_PHONE_NUMBERS) + val birthdaysJson = cursor.getStringValue(COL_BIRTHDAYS) + val anniversariesJson = cursor.getStringValue(COL_ANNIVERSARIES) val token = object : TypeToken>() {}.type val phoneNumbers = Gson().fromJson>(phoneNumbersJson, token) ?: ArrayList() - val contact = SimpleContact(rawId, contactId, name, photoUri, phoneNumbers) + val birthdays = Gson().fromJson>(birthdaysJson, token) ?: ArrayList() + val anniversaries = Gson().fromJson>(anniversariesJson, token) ?: ArrayList() + + val contact = SimpleContact(rawId, contactId, name, photoUri, phoneNumbers, birthdays, anniversaries) contacts.add(contact) } while (cursor.moveToNext()) } diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/helpers/SimpleContactsHelper.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/helpers/SimpleContactsHelper.kt index 60dfdf92a..fca10229e 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/helpers/SimpleContactsHelper.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/helpers/SimpleContactsHelper.kt @@ -12,6 +12,7 @@ import android.provider.ContactsContract.* import android.provider.ContactsContract.CommonDataKinds.Organization import android.provider.ContactsContract.CommonDataKinds.StructuredName import android.text.TextUtils +import android.util.SparseArray import android.widget.ImageView import android.widget.TextView import com.bumptech.glide.Glide @@ -47,6 +48,20 @@ class SimpleContactsHelper(val context: Context) { it.phoneNumbers.first().substring(startIndex) }.distinctBy { it.rawId }.toMutableList() as ArrayList + val birthdays = getContactEvents(true) + var size = birthdays.size() + for (i in 0 until size) { + val key = birthdays.keyAt(i) + allContacts.firstOrNull { it.rawId == key }?.birthdays = birthdays.valueAt(i) + } + + val anniversaries = getContactEvents(false) + size = anniversaries.size() + for (i in 0 until size) { + val key = anniversaries.keyAt(i) + allContacts.firstOrNull { it.rawId == key }?.anniversaries = anniversaries.valueAt(i) + } + allContacts.sort() callback(allContacts) } @@ -101,7 +116,7 @@ class SimpleContactsHelper(val context: Context) { } val fullName = TextUtils.join(" ", names) - val contact = SimpleContact(rawId, contactId, fullName, photoUri, ArrayList()) + val contact = SimpleContact(rawId, contactId, fullName, photoUri, ArrayList(), ArrayList(), ArrayList()) contacts.add(contact) } } @@ -112,7 +127,7 @@ class SimpleContactsHelper(val context: Context) { val jobTitle = cursor.getStringValue(Organization.TITLE) ?: "" if (company.isNotEmpty() || jobTitle.isNotEmpty()) { val fullName = "$company $jobTitle".trim() - val contact = SimpleContact(rawId, contactId, fullName, photoUri, ArrayList()) + val contact = SimpleContact(rawId, contactId, fullName, photoUri, ArrayList(), ArrayList(), ArrayList()) contacts.add(contact) } } @@ -139,13 +154,40 @@ class SimpleContactsHelper(val context: Context) { val rawId = cursor.getIntValue(Data.RAW_CONTACT_ID) val contactId = cursor.getIntValue(Data.CONTACT_ID) if (contacts.firstOrNull { it.rawId == rawId } == null) { - contacts.add(SimpleContact(rawId, contactId, "", "", ArrayList())) + val contact = SimpleContact(rawId, contactId, "", "", ArrayList(), ArrayList(), ArrayList()) + contacts.add(contact) } contacts.firstOrNull { it.rawId == rawId }?.phoneNumbers?.add(phoneNumber) } return contacts } + fun getContactEvents(getBirthdays: Boolean): SparseArray> { + val eventDates = SparseArray>() + val uri = Data.CONTENT_URI + val projection = arrayOf( + Data.RAW_CONTACT_ID, + CommonDataKinds.Event.START_DATE + ) + + val selection = "${CommonDataKinds.Event.MIMETYPE} = ? AND ${CommonDataKinds.Event.TYPE} = ?" + val requiredType = if (getBirthdays) CommonDataKinds.Event.TYPE_BIRTHDAY.toString() else CommonDataKinds.Event.TYPE_ANNIVERSARY.toString() + val selectionArgs = arrayOf(CommonDataKinds.Event.CONTENT_ITEM_TYPE, requiredType) + + context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> + val id = cursor.getIntValue(Data.RAW_CONTACT_ID) + val startDate = cursor.getStringValue(CommonDataKinds.Event.START_DATE) ?: return@queryCursor + + if (eventDates[id] == null) { + eventDates.put(id, ArrayList()) + } + + eventDates[id]!!.add(startDate) + } + + return eventDates + } + fun getNameFromPhoneNumber(number: String): String { if (!context.hasPermission(PERMISSION_READ_CONTACTS)) { return number diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/models/SimpleContact.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/models/SimpleContact.kt index 906401672..071230973 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/models/SimpleContact.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/models/SimpleContact.kt @@ -4,7 +4,8 @@ import android.telephony.PhoneNumberUtils import com.simplemobiletools.commons.extensions.normalizePhoneNumber import com.simplemobiletools.commons.extensions.normalizeString -data class SimpleContact(val rawId: Int, val contactId: Int, var name: String, var photoUri: String, var phoneNumbers: ArrayList) : Comparable { +data class SimpleContact(val rawId: Int, val contactId: Int, var name: String, var photoUri: String, var phoneNumbers: ArrayList, + var birthdays: ArrayList, var anniversaries: ArrayList) : Comparable { override fun compareTo(other: SimpleContact): Int { val firstString = name.normalizeString() val secondString = other.name.normalizeString()