Extract all properties when adding contact attachment
This commit is contained in:
parent
54f7c5980b
commit
ecdbc9036a
|
@ -57,9 +57,6 @@ import com.simplemobiletools.smsmessenger.dialogs.ScheduleMessageDialog
|
||||||
import com.simplemobiletools.smsmessenger.extensions.*
|
import com.simplemobiletools.smsmessenger.extensions.*
|
||||||
import com.simplemobiletools.smsmessenger.helpers.*
|
import com.simplemobiletools.smsmessenger.helpers.*
|
||||||
import com.simplemobiletools.smsmessenger.models.*
|
import com.simplemobiletools.smsmessenger.models.*
|
||||||
import ezvcard.VCard
|
|
||||||
import ezvcard.property.FormattedName
|
|
||||||
import ezvcard.property.Telephone
|
|
||||||
import kotlinx.android.synthetic.main.activity_thread.*
|
import kotlinx.android.synthetic.main.activity_thread.*
|
||||||
import kotlinx.android.synthetic.main.item_selected_contact.view.*
|
import kotlinx.android.synthetic.main.item_selected_contact.view.*
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
@ -240,7 +237,7 @@ class ThreadActivity : SimpleActivity() {
|
||||||
addAttachment(data)
|
addAttachment(data)
|
||||||
}
|
}
|
||||||
PICK_CONTACT_INTENT -> if (data != null) {
|
PICK_CONTACT_INTENT -> if (data != null) {
|
||||||
handleContactAttachment(data)
|
addContactAttachment(data)
|
||||||
}
|
}
|
||||||
PICK_SAVE_FILE_INTENT -> if (data != null) {
|
PICK_SAVE_FILE_INTENT -> if (data != null) {
|
||||||
saveAttachment(resultData)
|
saveAttachment(resultData)
|
||||||
|
@ -817,17 +814,16 @@ class ThreadActivity : SimpleActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createTemporaryFile(extension: String = ".jpg"): File {
|
private fun getAttachmentsDir(): File {
|
||||||
val outputDirectory = File(cacheDir, "captured").apply {
|
return File(cacheDir, "attachments").apply {
|
||||||
if (!exists()) {
|
if (!exists()) {
|
||||||
mkdirs()
|
mkdirs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return File.createTempFile("attachment_", extension, outputDirectory)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchCapturePhotoIntent() {
|
private fun launchCapturePhotoIntent() {
|
||||||
val imageFile = createTemporaryFile()
|
val imageFile = File.createTempFile("attachment_", ".jpg", getAttachmentsDir())
|
||||||
capturedImageUri = getMyFileUri(imageFile)
|
capturedImageUri = getMyFileUri(imageFile)
|
||||||
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
|
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
|
||||||
putExtra(MediaStore.EXTRA_OUTPUT, capturedImageUri)
|
putExtra(MediaStore.EXTRA_OUTPUT, capturedImageUri)
|
||||||
|
@ -874,26 +870,30 @@ class ThreadActivity : SimpleActivity() {
|
||||||
launchActivityForResult(intent, PICK_CONTACT_INTENT)
|
launchActivityForResult(intent, PICK_CONTACT_INTENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleContactAttachment(contactUri: Uri) {
|
private fun addContactAttachment(contactUri: Uri) {
|
||||||
val projection = arrayOf(
|
ensureBackgroundThread {
|
||||||
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
|
val contact = ContactsHelper(this).getContactFromUri(contactUri)
|
||||||
ContactsContract.CommonDataKinds.Phone.NUMBER
|
if (contact != null) {
|
||||||
)
|
val outputFile = File(getAttachmentsDir(), "${contact.contactId}.vcf")
|
||||||
queryCursor(contactUri, projection, null, null, null) { cursor ->
|
val outputStream = outputFile.outputStream()
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
val nameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
|
VcfExporter().exportContacts(
|
||||||
val numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
|
activity = this,
|
||||||
val name = cursor.getString(nameIndex)
|
outputStream = outputStream,
|
||||||
val number = cursor.getString(numberIndex)
|
contacts = arrayListOf(contact),
|
||||||
// todo: export all properties using VcfExporter
|
showExportingToast = false,
|
||||||
val vCard = VCard()
|
) {
|
||||||
vCard.addTelephoneNumber(Telephone(number))
|
if (it == VcfExporter.ExportResult.EXPORT_OK) {
|
||||||
vCard.addFormattedName(FormattedName(name))
|
val vCardUri = getMyFileUri(outputFile)
|
||||||
val file = createTemporaryFile(".vcf")
|
runOnUiThread {
|
||||||
val outputStream = file.outputStream()
|
addAttachment(vCardUri)
|
||||||
vCard.write(outputStream)
|
}
|
||||||
val vCardUri = getMyFileUri(file)
|
} else {
|
||||||
addAttachment(vCardUri)
|
toast(R.string.unknown_error_occurred)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast(R.string.unknown_error_occurred)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -914,6 +914,7 @@ class ThreadActivity : SimpleActivity() {
|
||||||
if (adapter == null) {
|
if (adapter == null) {
|
||||||
adapter = AttachmentsAdapter(
|
adapter = AttachmentsAdapter(
|
||||||
activity = this,
|
activity = this,
|
||||||
|
recyclerView = thread_attachments_recyclerview,
|
||||||
onItemClick = {},
|
onItemClick = {},
|
||||||
onAttachmentsRemoved = {
|
onAttachmentsRemoved = {
|
||||||
thread_attachments_recyclerview.beGone()
|
thread_attachments_recyclerview.beGone()
|
||||||
|
|
|
@ -31,6 +31,7 @@ import kotlinx.android.synthetic.main.item_remove_attachment_button.view.*
|
||||||
|
|
||||||
class AttachmentsAdapter(
|
class AttachmentsAdapter(
|
||||||
val activity: BaseSimpleActivity,
|
val activity: BaseSimpleActivity,
|
||||||
|
val recyclerView: RecyclerView,
|
||||||
val onItemClick: (AttachmentSelection) -> Unit,
|
val onItemClick: (AttachmentSelection) -> Unit,
|
||||||
val onAttachmentsRemoved: () -> Unit,
|
val onAttachmentsRemoved: () -> Unit,
|
||||||
val onReady: (() -> Unit)
|
val onReady: (() -> Unit)
|
||||||
|
@ -45,8 +46,10 @@ class AttachmentsAdapter(
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
attachments.clear()
|
attachments.clear()
|
||||||
submitList(ArrayList())
|
submitList(emptyList())
|
||||||
onAttachmentsRemoved()
|
recyclerView.onGlobalLayout {
|
||||||
|
onAttachmentsRemoved()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addAttachment(attachment: AttachmentSelection) {
|
fun addAttachment(attachment: AttachmentSelection) {
|
||||||
|
@ -58,9 +61,9 @@ class AttachmentsAdapter(
|
||||||
private fun removeAttachment(attachment: AttachmentSelection) {
|
private fun removeAttachment(attachment: AttachmentSelection) {
|
||||||
attachments.removeAll { AttachmentSelection.areItemsTheSame(it, attachment) }
|
attachments.removeAll { AttachmentSelection.areItemsTheSame(it, attachment) }
|
||||||
if (attachments.isEmpty()) {
|
if (attachments.isEmpty()) {
|
||||||
onAttachmentsRemoved()
|
clear()
|
||||||
} else {
|
} else {
|
||||||
submitList(ArrayList(attachments))
|
submitList(attachments.toList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,449 @@
|
||||||
|
package com.simplemobiletools.smsmessenger.helpers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.ContactsContract
|
||||||
|
import android.provider.ContactsContract.CommonDataKinds.*
|
||||||
|
import android.provider.ContactsContract.Data
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.SparseArray
|
||||||
|
import com.simplemobiletools.commons.extensions.*
|
||||||
|
import com.simplemobiletools.commons.models.PhoneNumber
|
||||||
|
import com.simplemobiletools.commons.models.contacts.*
|
||||||
|
import com.simplemobiletools.commons.models.contacts.Email
|
||||||
|
import com.simplemobiletools.commons.models.contacts.Event
|
||||||
|
import com.simplemobiletools.commons.models.contacts.Organization
|
||||||
|
import com.simplemobiletools.commons.overloads.times
|
||||||
|
|
||||||
|
// based on the ContactsHelper from Simple-Contacts
|
||||||
|
class ContactsHelper(val context: Context) {
|
||||||
|
private var displayContactSources = ArrayList<String>()
|
||||||
|
|
||||||
|
fun getContactFromUri(uri: Uri): Contact? {
|
||||||
|
val projection = arrayOf(Data.RAW_CONTACT_ID)
|
||||||
|
val cursor = context.contentResolver.query(uri, projection, null, null, null)
|
||||||
|
cursor?.use {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||||
|
return getContactWithId(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getContactWithId(id: Int): Contact? {
|
||||||
|
if (id == 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val selection = "(${Data.MIMETYPE} = ? OR ${Data.MIMETYPE} = ?) AND ${Data.RAW_CONTACT_ID} = ?"
|
||||||
|
val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE, id.toString())
|
||||||
|
return parseContactCursor(selection, selectionArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseContactCursor(selection: String, selectionArgs: Array<String>): Contact? {
|
||||||
|
val storedGroups = getDeviceStoredGroups()
|
||||||
|
val uri = Data.CONTENT_URI
|
||||||
|
val projection = getContactProjection()
|
||||||
|
|
||||||
|
val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||||
|
cursor?.use {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||||
|
|
||||||
|
var prefix = ""
|
||||||
|
var firstName = ""
|
||||||
|
var middleName = ""
|
||||||
|
var surname = ""
|
||||||
|
var suffix = ""
|
||||||
|
val mimetype = cursor.getStringValue(Data.MIMETYPE)
|
||||||
|
|
||||||
|
// ignore names at Organization type contacts
|
||||||
|
if (mimetype == StructuredName.CONTENT_ITEM_TYPE) {
|
||||||
|
prefix = cursor.getStringValue(StructuredName.PREFIX) ?: ""
|
||||||
|
firstName = cursor.getStringValue(StructuredName.GIVEN_NAME) ?: ""
|
||||||
|
middleName = cursor.getStringValue(StructuredName.MIDDLE_NAME) ?: ""
|
||||||
|
surname = cursor.getStringValue(StructuredName.FAMILY_NAME) ?: ""
|
||||||
|
suffix = cursor.getStringValue(StructuredName.SUFFIX) ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val nickname = getNicknames(id)[id] ?: ""
|
||||||
|
val photoUri = cursor.getStringValue(Phone.PHOTO_URI) ?: ""
|
||||||
|
val number = getPhoneNumbers(id)[id] ?: ArrayList()
|
||||||
|
val emails = getEmails(id)[id] ?: ArrayList()
|
||||||
|
val addresses = getAddresses(id)[id] ?: ArrayList()
|
||||||
|
val events = getEvents(id)[id] ?: ArrayList()
|
||||||
|
val notes = getNotes(id)[id] ?: ""
|
||||||
|
val accountName = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) ?: ""
|
||||||
|
val starred = cursor.getIntValue(StructuredName.STARRED)
|
||||||
|
val ringtone = cursor.getStringValue(StructuredName.CUSTOM_RINGTONE)
|
||||||
|
val contactId = cursor.getIntValue(Data.CONTACT_ID)
|
||||||
|
val groups = getContactGroups(storedGroups, contactId)[contactId] ?: ArrayList()
|
||||||
|
val thumbnailUri = cursor.getStringValue(StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
|
||||||
|
val organization = getOrganizations(id)[id] ?: Organization("", "")
|
||||||
|
val websites = getWebsites(id)[id] ?: ArrayList()
|
||||||
|
val ims = getIMs(id)[id] ?: ArrayList()
|
||||||
|
return Contact(
|
||||||
|
id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, number, emails, addresses, events,
|
||||||
|
accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, ims, mimetype, ringtone
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPhoneNumbers(contactId: Int? = null): SparseArray<ArrayList<PhoneNumber>> {
|
||||||
|
val phoneNumbers = SparseArray<ArrayList<PhoneNumber>>()
|
||||||
|
val uri = Phone.CONTENT_URI
|
||||||
|
val projection = arrayOf(
|
||||||
|
Data.RAW_CONTACT_ID,
|
||||||
|
Phone.NUMBER,
|
||||||
|
Phone.NORMALIZED_NUMBER,
|
||||||
|
Phone.TYPE,
|
||||||
|
Phone.LABEL,
|
||||||
|
Phone.IS_PRIMARY
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?"
|
||||||
|
val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
|
||||||
|
|
||||||
|
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||||
|
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||||
|
val number = cursor.getStringValue(Phone.NUMBER) ?: return@queryCursor
|
||||||
|
val normalizedNumber = cursor.getStringValue(Phone.NORMALIZED_NUMBER) ?: number.normalizePhoneNumber()
|
||||||
|
val type = cursor.getIntValue(Phone.TYPE)
|
||||||
|
val label = cursor.getStringValue(Phone.LABEL) ?: ""
|
||||||
|
val isPrimary = cursor.getIntValue(Phone.IS_PRIMARY) != 0
|
||||||
|
|
||||||
|
if (phoneNumbers[id] == null) {
|
||||||
|
phoneNumbers.put(id, ArrayList())
|
||||||
|
}
|
||||||
|
|
||||||
|
val phoneNumber = PhoneNumber(number, type, label, normalizedNumber, isPrimary)
|
||||||
|
phoneNumbers[id].add(phoneNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
return phoneNumbers
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNicknames(contactId: Int? = null): SparseArray<String> {
|
||||||
|
val nicknames = SparseArray<String>()
|
||||||
|
val uri = Data.CONTENT_URI
|
||||||
|
val projection = arrayOf(
|
||||||
|
Data.RAW_CONTACT_ID,
|
||||||
|
Nickname.NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = getSourcesSelection(true, contactId != null)
|
||||||
|
val selectionArgs = getSourcesSelectionArgs(Nickname.CONTENT_ITEM_TYPE, contactId)
|
||||||
|
|
||||||
|
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||||
|
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||||
|
val nickname = cursor.getStringValue(Nickname.NAME) ?: return@queryCursor
|
||||||
|
nicknames.put(id, nickname)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nicknames
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getEmails(contactId: Int? = null): SparseArray<ArrayList<Email>> {
|
||||||
|
val emails = SparseArray<ArrayList<Email>>()
|
||||||
|
val uri = ContactsContract.CommonDataKinds.Email.CONTENT_URI
|
||||||
|
val projection = arrayOf(
|
||||||
|
Data.RAW_CONTACT_ID,
|
||||||
|
ContactsContract.CommonDataKinds.Email.DATA,
|
||||||
|
ContactsContract.CommonDataKinds.Email.TYPE,
|
||||||
|
ContactsContract.CommonDataKinds.Email.LABEL
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?"
|
||||||
|
val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
|
||||||
|
|
||||||
|
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||||
|
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||||
|
val email = cursor.getStringValue(ContactsContract.CommonDataKinds.Email.DATA) ?: return@queryCursor
|
||||||
|
val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Email.TYPE)
|
||||||
|
val label = cursor.getStringValue(ContactsContract.CommonDataKinds.Email.LABEL) ?: ""
|
||||||
|
|
||||||
|
if (emails[id] == null) {
|
||||||
|
emails.put(id, ArrayList())
|
||||||
|
}
|
||||||
|
|
||||||
|
emails[id]!!.add(Email(email, type, label))
|
||||||
|
}
|
||||||
|
|
||||||
|
return emails
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAddresses(contactId: Int? = null): SparseArray<ArrayList<Address>> {
|
||||||
|
val addresses = SparseArray<ArrayList<Address>>()
|
||||||
|
val uri = StructuredPostal.CONTENT_URI
|
||||||
|
val projection = arrayOf(
|
||||||
|
Data.RAW_CONTACT_ID,
|
||||||
|
StructuredPostal.FORMATTED_ADDRESS,
|
||||||
|
StructuredPostal.TYPE,
|
||||||
|
StructuredPostal.LABEL
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?"
|
||||||
|
val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
|
||||||
|
|
||||||
|
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||||
|
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||||
|
val address = cursor.getStringValue(StructuredPostal.FORMATTED_ADDRESS) ?: return@queryCursor
|
||||||
|
val type = cursor.getIntValue(StructuredPostal.TYPE)
|
||||||
|
val label = cursor.getStringValue(StructuredPostal.LABEL) ?: ""
|
||||||
|
|
||||||
|
if (addresses[id] == null) {
|
||||||
|
addresses.put(id, ArrayList())
|
||||||
|
}
|
||||||
|
|
||||||
|
addresses[id]!!.add(Address(address, type, label))
|
||||||
|
}
|
||||||
|
|
||||||
|
return addresses
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getIMs(contactId: Int? = null): SparseArray<ArrayList<IM>> {
|
||||||
|
val IMs = SparseArray<ArrayList<IM>>()
|
||||||
|
val uri = Data.CONTENT_URI
|
||||||
|
val projection = arrayOf(
|
||||||
|
Data.RAW_CONTACT_ID,
|
||||||
|
Im.DATA,
|
||||||
|
Im.PROTOCOL,
|
||||||
|
Im.CUSTOM_PROTOCOL
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = getSourcesSelection(true, contactId != null)
|
||||||
|
val selectionArgs = getSourcesSelectionArgs(Im.CONTENT_ITEM_TYPE, contactId)
|
||||||
|
|
||||||
|
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||||
|
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||||
|
val IM = cursor.getStringValue(Im.DATA) ?: return@queryCursor
|
||||||
|
val type = cursor.getIntValue(Im.PROTOCOL)
|
||||||
|
val label = cursor.getStringValue(Im.CUSTOM_PROTOCOL) ?: ""
|
||||||
|
|
||||||
|
if (IMs[id] == null) {
|
||||||
|
IMs.put(id, ArrayList())
|
||||||
|
}
|
||||||
|
|
||||||
|
IMs[id]!!.add(IM(IM, type, label))
|
||||||
|
}
|
||||||
|
|
||||||
|
return IMs
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getEvents(contactId: Int? = null): SparseArray<ArrayList<Event>> {
|
||||||
|
val events = SparseArray<ArrayList<Event>>()
|
||||||
|
val uri = Data.CONTENT_URI
|
||||||
|
val projection = arrayOf(
|
||||||
|
Data.RAW_CONTACT_ID,
|
||||||
|
ContactsContract.CommonDataKinds.Event.START_DATE,
|
||||||
|
ContactsContract.CommonDataKinds.Event.TYPE
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = getSourcesSelection(true, contactId != null)
|
||||||
|
val selectionArgs = getSourcesSelectionArgs(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, contactId)
|
||||||
|
|
||||||
|
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||||
|
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||||
|
val startDate = cursor.getStringValue(ContactsContract.CommonDataKinds.Event.START_DATE) ?: return@queryCursor
|
||||||
|
val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Event.TYPE)
|
||||||
|
|
||||||
|
if (events[id] == null) {
|
||||||
|
events.put(id, ArrayList())
|
||||||
|
}
|
||||||
|
|
||||||
|
events[id]!!.add(Event(startDate, type))
|
||||||
|
}
|
||||||
|
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNotes(contactId: Int? = null): SparseArray<String> {
|
||||||
|
val notes = SparseArray<String>()
|
||||||
|
val uri = Data.CONTENT_URI
|
||||||
|
val projection = arrayOf(
|
||||||
|
Data.RAW_CONTACT_ID,
|
||||||
|
Note.NOTE
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = getSourcesSelection(true, contactId != null)
|
||||||
|
val selectionArgs = getSourcesSelectionArgs(Note.CONTENT_ITEM_TYPE, contactId)
|
||||||
|
|
||||||
|
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||||
|
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||||
|
val note = cursor.getStringValue(Note.NOTE) ?: return@queryCursor
|
||||||
|
notes.put(id, note)
|
||||||
|
}
|
||||||
|
|
||||||
|
return notes
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOrganizations(contactId: Int? = null): SparseArray<Organization> {
|
||||||
|
val organizations = SparseArray<Organization>()
|
||||||
|
val uri = Data.CONTENT_URI
|
||||||
|
val projection = arrayOf(
|
||||||
|
Data.RAW_CONTACT_ID,
|
||||||
|
ContactsContract.CommonDataKinds.Organization.COMPANY,
|
||||||
|
ContactsContract.CommonDataKinds.Organization.TITLE
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = getSourcesSelection(true, contactId != null)
|
||||||
|
val selectionArgs = getSourcesSelectionArgs(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE, contactId)
|
||||||
|
|
||||||
|
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||||
|
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||||
|
val company = cursor.getStringValue(ContactsContract.CommonDataKinds.Organization.COMPANY) ?: ""
|
||||||
|
val title = cursor.getStringValue(ContactsContract.CommonDataKinds.Organization.TITLE) ?: ""
|
||||||
|
if (company.isEmpty() && title.isEmpty()) {
|
||||||
|
return@queryCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
val organization = Organization(company, title)
|
||||||
|
organizations.put(id, organization)
|
||||||
|
}
|
||||||
|
|
||||||
|
return organizations
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getWebsites(contactId: Int? = null): SparseArray<ArrayList<String>> {
|
||||||
|
val websites = SparseArray<ArrayList<String>>()
|
||||||
|
val uri = Data.CONTENT_URI
|
||||||
|
val projection = arrayOf(
|
||||||
|
Data.RAW_CONTACT_ID,
|
||||||
|
Website.URL
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = getSourcesSelection(true, contactId != null)
|
||||||
|
val selectionArgs = getSourcesSelectionArgs(Website.CONTENT_ITEM_TYPE, contactId)
|
||||||
|
|
||||||
|
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||||
|
val id = cursor.getIntValue(Data.RAW_CONTACT_ID)
|
||||||
|
val url = cursor.getStringValue(Website.URL) ?: return@queryCursor
|
||||||
|
|
||||||
|
if (websites[id] == null) {
|
||||||
|
websites.put(id, ArrayList())
|
||||||
|
}
|
||||||
|
|
||||||
|
websites[id]!!.add(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
return websites
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDeviceStoredGroups(): java.util.ArrayList<Group> {
|
||||||
|
val groups = java.util.ArrayList<Group>()
|
||||||
|
|
||||||
|
val uri = ContactsContract.Groups.CONTENT_URI
|
||||||
|
val projection = arrayOf(
|
||||||
|
ContactsContract.Groups._ID,
|
||||||
|
ContactsContract.Groups.TITLE,
|
||||||
|
ContactsContract.Groups.SYSTEM_ID
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = "${ContactsContract.Groups.AUTO_ADD} = ? AND ${ContactsContract.Groups.FAVORITES} = ?"
|
||||||
|
val selectionArgs = arrayOf("0", "0")
|
||||||
|
|
||||||
|
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||||
|
val id = cursor.getLongValue(ContactsContract.Groups._ID)
|
||||||
|
val title = cursor.getStringValue(ContactsContract.Groups.TITLE) ?: return@queryCursor
|
||||||
|
|
||||||
|
val systemId = cursor.getStringValue(ContactsContract.Groups.SYSTEM_ID)
|
||||||
|
if (groups.map { it.title }.contains(title) && systemId != null) {
|
||||||
|
return@queryCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
groups.add(Group(id, title))
|
||||||
|
}
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getContactGroups(storedGroups: ArrayList<Group>, contactId: Int? = null): SparseArray<ArrayList<Group>> {
|
||||||
|
val groups = SparseArray<ArrayList<Group>>()
|
||||||
|
|
||||||
|
val uri = Data.CONTENT_URI
|
||||||
|
val projection = arrayOf(
|
||||||
|
Data.CONTACT_ID,
|
||||||
|
Data.DATA1
|
||||||
|
)
|
||||||
|
|
||||||
|
val selection = getSourcesSelection(true, contactId != null, false)
|
||||||
|
val selectionArgs = getSourcesSelectionArgs(GroupMembership.CONTENT_ITEM_TYPE, contactId)
|
||||||
|
|
||||||
|
context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor ->
|
||||||
|
val id = cursor.getIntValue(Data.CONTACT_ID)
|
||||||
|
val newRowId = cursor.getLongValue(Data.DATA1)
|
||||||
|
|
||||||
|
val groupTitle = storedGroups.firstOrNull { it.id == newRowId }?.title ?: return@queryCursor
|
||||||
|
val group = Group(newRowId, groupTitle)
|
||||||
|
if (groups[id] == null) {
|
||||||
|
groups.put(id, ArrayList())
|
||||||
|
}
|
||||||
|
groups[id]!!.add(group)
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getQuestionMarks() = ("?," * displayContactSources.filter { it.isNotEmpty() }.size).trimEnd(',')
|
||||||
|
|
||||||
|
private fun getSourcesSelection(addMimeType: Boolean = false, addContactId: Boolean = false, useRawContactId: Boolean = true): String {
|
||||||
|
val strings = ArrayList<String>()
|
||||||
|
if (addMimeType) {
|
||||||
|
strings.add("${Data.MIMETYPE} = ?")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addContactId) {
|
||||||
|
strings.add("${if (useRawContactId) Data.RAW_CONTACT_ID else Data.CONTACT_ID} = ?")
|
||||||
|
} else {
|
||||||
|
// sometimes local device storage has null account_name, handle it properly
|
||||||
|
val accountNameString = StringBuilder()
|
||||||
|
if (displayContactSources.contains("")) {
|
||||||
|
accountNameString.append("(")
|
||||||
|
}
|
||||||
|
accountNameString.append("${ContactsContract.RawContacts.ACCOUNT_NAME} IN (${getQuestionMarks()})")
|
||||||
|
if (displayContactSources.contains("")) {
|
||||||
|
accountNameString.append(" OR ${ContactsContract.RawContacts.ACCOUNT_NAME} IS NULL)")
|
||||||
|
}
|
||||||
|
strings.add(accountNameString.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
return TextUtils.join(" AND ", strings)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSourcesSelectionArgs(mimetype: String? = null, contactId: Int? = null): Array<String> {
|
||||||
|
val args = ArrayList<String>()
|
||||||
|
|
||||||
|
if (mimetype != null) {
|
||||||
|
args.add(mimetype)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contactId != null) {
|
||||||
|
args.add(contactId.toString())
|
||||||
|
} else {
|
||||||
|
args.addAll(displayContactSources.filter { it.isNotEmpty() })
|
||||||
|
}
|
||||||
|
|
||||||
|
return args.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getContactProjection() = arrayOf(
|
||||||
|
Data.MIMETYPE,
|
||||||
|
Data.CONTACT_ID,
|
||||||
|
Data.RAW_CONTACT_ID,
|
||||||
|
StructuredName.PREFIX,
|
||||||
|
StructuredName.GIVEN_NAME,
|
||||||
|
StructuredName.MIDDLE_NAME,
|
||||||
|
StructuredName.FAMILY_NAME,
|
||||||
|
StructuredName.SUFFIX,
|
||||||
|
StructuredName.PHOTO_URI,
|
||||||
|
StructuredName.PHOTO_THUMBNAIL_URI,
|
||||||
|
StructuredName.STARRED,
|
||||||
|
StructuredName.CUSTOM_RINGTONE,
|
||||||
|
ContactsContract.RawContacts.ACCOUNT_NAME,
|
||||||
|
ContactsContract.RawContacts.ACCOUNT_TYPE
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<paths>
|
<paths>
|
||||||
<cache-path name="compressed_files" path="compressed/"/>
|
<cache-path name="compressed_files" path="compressed/"/>
|
||||||
<cache-path name="captured_files" path="captured/"/>
|
<cache-path name="attachment_files" path="attachments/"/>
|
||||||
</paths>
|
</paths>
|
||||||
|
|
Loading…
Reference in New Issue