My app is scanning through SMS messages and calculating metrics, which it then stores in a database keyed by the contact ID associated with the phone number on the SMS. This first contact ID is looked up using the PhoneLookup
mechanism. At a later time I also let the user select a contact using the contact picker intent. But even when the same person is selected, the contact ID that comes back from the picker is different from the one obtained via PhoneLookup
. Why is this?
Here's an activity that shows this effect. Set PHONE_NUMBER
equal to the phone number of a known contact on your phone. Then tap the "Pick Contact" button and select the same contact. For me, the contact IDs from these two methods are different.
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.ContactsContract
import android.support.v7.app.AppCompatActivity
import android.util.Log
import com.example.R
import kotlinx.android.synthetic.main.activity_contact_idtest.*
class ContactIDTest : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_contact_idtest)
}
companion object {
const val TAG = "ContactIDTest"
const val RQST_PICK_CONTACT = 1
const val PHONE_NUMBER = "5551234567"
}
override fun onResume() {
super.onResume()
cmdPickContact.setOnClickListener {
val pickContactIntent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
pickContactIntent.type = ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE // Show user only contacts w/ phone numbers
startActivityForResult(pickContactIntent, RQST_PICK_CONTACT)
}
contactForPhoneNumber(PHONE_NUMBER, { contactID, displayName, photoThumbnailUri ->
txtContactID.text = contactID.toString()
txtContactName.text = displayName
}, {
Log.e(TAG, "Error getting contact for phone number")
})
}
private fun contactForPhoneNumber(phoneNumber: String, callback: (contactID: Long, displayName: String?, photoThumbnailUri: String?) -> Unit, error: () -> Unit) {
val contactURI = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber))
try {
val cursor = contentResolver.query(contactURI,
arrayOf(ContactsContract.PhoneLookup.CONTACT_ID, ContactsContract.PhoneLookup.DISPLAY_NAME, ContactsContract.PhoneLookup.PHOTO_THUMBNAIL_URI),
null,
null,
null
)
if(cursor == null || cursor.count <= 0) {
error()
} else {
cursor.moveToFirst()
val contactID = cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup.CONTACT_ID))
val displayName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup.DISPLAY_NAME))
val photoThumbnailUri = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup.PHOTO_THUMBNAIL_URI))
cursor.close()
callback(contactID, displayName, photoThumbnailUri)
}
} catch(e: Exception) {
Log.e(TAG, "Error loading contact for phone number: $phoneNumber", e)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if(requestCode == RQST_PICK_CONTACT) {
if(resultCode == Activity.RESULT_OK) {
val contactData = data!!.data
val c = contentResolver.query(contactData, null, null, null, null)
if(c.moveToFirst()) {
val contactID = c.getLong(c.getColumnIndex(ContactsContract.Contacts._ID))
val displayName = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
txtPickedContactID.text = contactID.toString()
txtPickedContactName.text = displayName.toString()
}
c.close()
}
}
}
}
And the layout file:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layoutContactIDTest"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.ContactIDTest"
tools:layout_editor_absoluteY="81dp">
<TextView
android:id="@+id/txtContactName"
android:layout_width="wrap_content"
android:layout_height="21dp"
android:text="$CONTACT_NAME"
app:layout_constraintBottom_toTopOf="@+id/cmdPickContact"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txtContactID" />
<TextView
android:id="@+id/txtContactID"
android:layout_width="wrap_content"
android:layout_height="18dp"
android:text="$CONTACT_ID"
app:layout_constraintBottom_toTopOf="@+id/txtContactName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/cmdPickContact"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Pick Contact"
app:layout_constraintBottom_toTopOf="@+id/txtPickedContactID"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txtContactName" />
<TextView
android:id="@+id/txtPickedContactID"
android:layout_width="wrap_content"
android:layout_height="17dp"
android:text="$PICKED_CONTACT_ID"
app:layout_constraintBottom_toTopOf="@+id/txtPickedContactName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cmdPickContact" />
<TextView
android:id="@+id/txtPickedContactName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="$PICKED_CONTACT_NAME"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txtPickedContactID" />
</android.support.constraint.ConstraintLayout>
This code:
val pickContactIntent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
pickContactIntent.type = ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE
Will return a CommonDataKinds.Phone._ID
not a Contacts._ID