Obtaining Email Addresses of Specific Contact Without Permissions - android

I can easily get a list of every email address for every Contact using the following example snippets:
//...
private val getPerson = registerForActivityResult(PickContact()) {
it?.also { contactUri ->
val personDetails = ContactForPerson("", "", "")
val projection = arrayOf(
ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY, //String
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY,//String
ContactsContract.CommonDataKinds.Email.ADDRESS, //String
)
context?.contentResolver?.query(contactUri, projection, null, null, null)?.apply {
moveToFirst()
personDetails.apply {
uri = getStringOrNull(0)
name = getString(1)
email = getStringOrNull(2)
}
close()
}
}
}
//...
fab.setOnClickListener {
//...
getPerson.launch(0)
//...
}
//...
class PickContact : ActivityResultContract<Int, Uri?>() {
override fun createIntent(context: Context, input: Int?) =
Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI).also {
it.type = ContactsContract.CommonDataKinds.Email.CONTENT_TYPE
}
override fun parseResult(resultCode: Int, intent: Intent?): Uri? =
if (resultCode == RESULT_OK) intent?.data else null
}
The question is, since I already have some information about a Contact, is there a way for me to filter the giant list of every email address for every Contact to only show me the email addresses for a single Contact?
I noticed Get specific contact information from URI returned from Intent.ACTION_PICK, but the information is rather dated and it's not clear if the READ_CONTACTS permission is required, which is not desired.
Thank you.

It seems you're calling an EMAIL-PICKER and not a CONTACT-PICKER, by setting the intent type to Email.CONTENT_TYPE.
This means the user will be choosing a specific email in the device's default contacts app.
The result you're then getting from the picker is not a contactUri, rather a dataUri - i.e. a uri that points to a specific row in the Data table, which only allows you to get info about that specific row, in this case it must be an email row.
This also means your projection is a bit funny by using fields under CommonDataKinds.Phone.X, this doesn't matter too much as these fields are inherited from Data.CONTENT_URI, but to prevent confusion you should probably replace these with:
Data.LOOKUP_KEY,
Data.DISPLAY_NAME_PRIMARY,
Email.ADDRESS,
Now, if you want all the contact's email rather then a single one, you should not set the intent type - this will launch a contact picker which will allow you to get the contact's emails + phones + name by retrieving the Entity as shown here.

Related

Update contact android - Kotlin

I need to update contact first and last name from emulator contact list.
my code:
val contentResolver = APP_ACTIVITY?.contentResolver
val contentValues = ContentValues().apply {
put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,"$firstName")
put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,"$lastName")
}
Log.d("TAG", "editContactEmulator:$contentValues") //**prints: data2=Michael data3=Lou**
val contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId.toString().toLong())
Log.d("TAG", "editContactEmulator: $contactUri") // **prints:content://com.android.contacts/contacts/1**
val res = contentResolver?.update(contactUri,contentValues, null, null)
Log.d("TAG", "editContactEmulator : $res") **//RETURNS 0**
so, the new values ( first and last name) are correct, the uri is the correct uri of the contact I want to update. BUT res is still 0 which means it did not update any rows.
Any Ideas?
( I added READ_CONTACTS and WRITE_CONTACTS permission - so that not the problem)

How to remove duplicate entries in Android Contacts App which gets added when adding new contacts programatically?

I have an app which inserts/updates a contact in the phone book using below code, the code works but the problem I am facing is if there is similar contact with the same phone number eg. a WhatsApp, dou, Viber etc. contact then those app contact takes over my contacts DisplayName and merges together but due to this, we are having multiple duplicate entries in some versions of android like (Samsung, LG, MI A5 stock android) etc.
But works on some phones like MI Max and a few others, does anyone have a solution to this problem or is there I am missing some fields that need to be present to avoid duplicate contacts.
private fun insertContact(contact: Contact): Boolean {
try {
val operations = ArrayList<ContentProviderOperation>()
ContentProviderOperation.newInsert(RawContacts.CONTENT_URI).apply {
withValue(RawContacts.ACCOUNT_NAME, "abcd#gmail.com")
withValue(RawContacts.ACCOUNT_TYPE, "google.com")
operations.add(build())
}
ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
withValueBackReference(Data.RAW_CONTACT_ID, 0)
withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
withValue(StructuredName.GIVEN_NAME, contact.firstName)
withValue(StructuredName.FAMILY_NAME, contact.lastName)
withValue(StructuredName.SUFFIX, "AppName")
operations.add(build())
}
addUpdatePhone(operations, contact.phoneNumbers)
//similar function for other fields email, address, birthday, profilePic etc
val results = context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
return true
} catch (e: Exception) {
LOG.e( "Error inserting contact")
return false
}
}
private fun updateContact(contact: contact, rawContactId: String): Boolean {
try {
val operations = ArrayList<ContentProviderOperation>()
ContentProviderOperation.newUpdate(Data.CONTENT_URI).apply {
val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ?"
val selectionArgs = arrayOf(rawContactId, StructuredName.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
withValue(StructuredName.GIVEN_NAME, contact.firstName)
withValue(StructuredName.FAMILY_NAME, contact.lastName)
withValue(StructuredName.SUFFIX, "AppName")
operations.add(build())
}
addUpdatePhone(operations, contact.phoneNumbers, true, rawContactId)
//similar function for other fields email, address, birthday, profilePic etc
context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
return true
} catch (e: Exception) {
LOG.e("Error updating contact")
return false
}
}
private fun addUpdatePhone(operations: ArrayList<ContentProviderOperation>, phoneNumbers: List<PhoneNumber>, isUpdate: Boolean = false, rawContactId: String = "") {
if(isUpdate) {
//delete old data with the given raw_contact_id
ContentProviderOperation.newDelete(Data.CONTENT_URI).apply {
val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? "
val selectionArgs = arrayOf(rawContactId, Phone.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
}
phoneNumbers.forEach {
//add new rows of phone number for the given raw_contact_id
ContentProviderOperation.newInsert(Data.CONTENT_URI).apply {
if(isUpdate) {
withValue(Data.RAW_CONTACT_ID, rawContactId)
} else {
withValueBackReference(Data.RAW_CONTACT_ID, 0)
}
withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
withValue(Phone.TYPE, it.type)
withValue(Phone.LABEL, it.label)
withValue(Phone.NUMBER, it.value)
withValue(Phone.NORMALIZED_NUMBER, it.value.normalizeNumber())
operations.add(build())
}
}
}
//similar functions as above for other fields
private fun otherUpdateFunAsAbove() {}
After adding a new RawContact, Android needs to make a decision if this is a brand new Contact or if there's already an existing contact that represents the same person.
Android has an algorithm, which changes and evolved with different OS versions, and might have been tweaked by makers like Samsung, but usually it looks for a very similar name with some other item (such as phone or email) that is either identical or very close.
In that case it'll merge the two contacts using RawContact Aggregation.
Apps can control this process via AggregationExceptions in which an app can state "Keep these two RawContacts separate" or "Keep these two RawContacts merged" regardless of that algorithm.
So what you're explaining would be normal behavior, your original name should still had been kept within the RawContacts.
In any way, I would not recommend trying to find a RawContact (i.e. in your selection clause) using an ID + Name, instead you should use just the RawContact ID.
If you can't find that RawContact ID, this still doesn't mean you should create a new one, instead use the RawContact's lookupUri that you need to store in your app, and get a possibly new ID from that, in a way similar to the approach suggested for Contacts.

How to detect android contact updated and sync to firestore

I am trying to get all the android contacts which been updated.
I am saving on firebase the last contact id i added and the last updated timestamp
I am using the next function to get back a cursor of all the updated contacts to compare with firebase server
private fun getUpdatedContacts(): Cursor? {
val projection = arrayOf(
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP)
val selection = ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " > ? AND " +
ContactsContract.Contacts._ID + "<= ?"
val selectionArgs = arrayOf(mFireContactDetails!!.lcu_ms.toString(), mFireContactDetails!!.lcid.toString())
val sortOrder = ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " ASC"
return mContentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
projection,
selection,
selectionArgs,
sortOrder)
}
But when i change one contact in my phone this cursor is returned MANY un-related contacts that i never used and mark them as changed. Last time when i just added a phone number to an existing contact, i got back from this cursor more than 50 contacts as been updated.
What is going on Android?? I am trying to sync contacts for the past 3 months now. Why is it so hard???
This is almost the same question with the same answer as your other question: When deleting a contact on android, other random contacts id's being changed
You have some assumptions on Contact IDs that you can't make - no one guarantees Contact IDs are incremental, and no one guarantees Contact IDs are stable, in fact they are definitely not.
You can use queried contact IDs while you're app is running, there is very small chance of them being changed within some minutes, but there is some chance of having IDs changed for existing users every once in a while.
Not only that, but the same ID can point to some contact today, and point to a completely different contact tomorrow.
If you keep some clone of the local contacts in the cloud, you should use the following composite IDs to reference contacts:
Contacts.CONTACT_ID, Contacts.LOOKUP_KEY, Contacts.DISPLAY_NAME
See my answer here for more details: How to uniquely identify a contact on ContactsContract.Contacts table
It's not a perfect solution, but it's the best we have
I been testing this solution for couple of days and it seems OK but i think i need to test it much more. If you using this method, do your own testing and above all, PLEASE LET ME KNOW IF I MISSED ANYTHING and don't be hurry to downgrade. Thx!
I built an App class that extents Application and implements the
ActivityLifecycleCallbacks. In which i create a ContactSync class for the
first time and activate it everytime the app goes to foregound
In ContactSync class, i am using Kotlin withContext(Dispatchers.IO) to suspend any code for easier flow
I use .get() to get all the contacts from firestore related to current user
at the .get() addOnSuccessListener, i add all the contacts to a HashMap with the normalized phone number as key and name + firestore id as values (using internal class)
While making the HashMap i also make sure there are no duplicates on firestore with smae phone number and if so delete them (using batch)
i then retrieve all the contacts from android phone. I sort them by NORMALIZED_NUMBER first and DISPLAY_NAME (will explain later)
I am now creating a batchArray with index and count to avoid exceeding the 500 limit
I start scanning through the contacts cursor,
I first get the normalized number, if not available (null) i create it my self using a function i made (it might be that a null value is only returned for phone numbers not in correct format, not sure)
I then compare the normalized number with previous cursor value. If the same i ignore it to avoid duplicates in firestore (remember the cursor is sorted by NORMALIZED_NUMBER)
I then check if the normalized number already in HashMap.
If in HashMap: i compare the name in HashMap to the cursor name. if different, i conclude the name was changed and i update the firestore contact in batch array (remember to increment counter and if exceeds 500 increase index). I then remove the normalized number from the HashMap to avoid its deletion later
If not in HashMap: i conclude the contact is new and i add it to firestore via batch
I iterate through all the cursor until completed.
When cursor complete i close it
Any remaining records found in HashMap are ones that were not found on firestore hence deleted. I iterate and delete them using batch
sync is done on the phone side
Now, since making the actual sync needs access to all users, i user firebase functions in node. I create 2 functions:
function that fires when new user is created (signed via phone)
function that fires when new contact document is created.
Both functions compare the users to the normalized number in document and if matching, writing the uid of that user to the firestore document "friend_uid" field.
Note you might have errors in these functions if you try to use them in free firebase plan. I suggest changing to Blaze plan and limit the charging to couple of dollars. By changing to Blaze, google also gives you free extras and avoid actual payment
By that, the sync is completed. The sync takes only couple of seconds
To display all the contacts which are users to the app, query all user contacts with "friend_uid" that are not null.
Some extra notes:
The .get() will retrieve all the contacts every time a sync is made. That might be a lot of reads if user has couple of hundreds contacts. To minimize, i use .get(Source.DEFAULT) when launching the app and .get(Source.CACHE) for the other times. Since these documents name and number only modified by user, i believe it will not be a problem most of the times (still testing)
To minimize the sync process as much as possible, i initiate it only if any contact changed its timestamp. I save the last timestamp to SharedPreferences and compare it. I found it mostly saves sync when app re-opened fast.
I also save the last user logged in. If any change in user, i re-initialize the current user contacts
Some source code (still testing, please let me know if any error):
private fun getContacts(): Cursor? {
val projection = arrayOf(
ContactsContract.CommonDataKinds.Phone._ID,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.CONTACT_LAST_UPDATED_TIMESTAMP)
//sort by NORMALIZED_NUMBER to detect duplicates and then by name to keep order and avoiding name change
val sortOrder = ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER + " ASC, " +
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC"
return mContentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
projection,
null,
null,
sortOrder)
}
private suspend fun syncContactsAsync() = withContext(Dispatchers.IO) {
if (isAnythingChanged() || mFirstRun) {
if (getValues() == Result.SUCCESS) {
myPrintln("values retrieved success")
} else {
myPrintln("values retrieved failed. Aborting.")
return#withContext
}
val cursor: Cursor? = getContacts()
if (cursor == null) {
myPrintln("cursor cannot be null")
mFireContactHashMap.clear()
return#withContext
}
if (cursor.count == 0) {
cursor.close()
mFireContactHashMap.clear()
myPrintln("cursor empty")
return#withContext
}
var contactName: String?
var internalContact: InternalContact?
val batchArray = mutableListOf(FirebaseFirestore.getInstance().batch())
var batchIndex = 0
var batchCount = 0
var normalizedNumber:String?
var prevNumber = ""
var firestoreId: String
while (cursor.moveToNext()) {
normalizedNumber = cursor.getString(COLUMN_UPDATED_NORMALIZED_NUMBER)
if (normalizedNumber == null) {
normalizedNumber = cursor.getString(COLUMN_UPDATED_PHONE_NUMBER)
normalizedNumber = Phone.getParsedPhoneNumber(mDeviceCountryIso,normalizedNumber,mContext)
}
//cursor sorted by normalized numbers so if same as previous, do not check
if (normalizedNumber != prevNumber) {
prevNumber = normalizedNumber
contactName = cursor.getString(COLUMN_UPDATED_DISPLAY_NAME)
internalContact = mFireContactHashMap[normalizedNumber]
//if phone number exists on firestore
if (internalContact != null) {
//if name changed, update in firestore
if (internalContact.name != contactName) {
myPrintln("updating $normalizedNumber from name: ${internalContact.name} to: $contactName")
batchArray[batchIndex].update(
mFireContactRef.document(internalContact.id),
FireContact.COLUMN_NAME,
contactName)
batchCount++
}
//remove to avoid deletions
mFireContactHashMap.remove(normalizedNumber)
} else {
//New item. Insert
if (normalizedNumber != mUserPhoneNumber) {
myPrintln("adding $normalizedNumber / $contactName")
firestoreId = mFireContactRef.document().id
batchArray[batchIndex].set(mFireContactRef.document(firestoreId),
FireContact(firestoreId, -1, contactName,
cursor.getString(COLUMN_UPDATED_PHONE_NUMBER),
normalizedNumber))
batchCount++
}
}
if (BATCH_HALF_MAX < batchCount ) {
batchArray += FirebaseFirestore.getInstance().batch()
batchCount = 0
batchIndex++
}
}
}
cursor.close()
//Remaining contacts not found on cursor so assumed deleted. Delete from firestore
mFireContactHashMap.forEach { (key, value) ->
myPrintln("deleting ${value.name} / $key")
batchArray[batchIndex].delete(mFireContactRef.document(value.id))
batchCount++
if (BATCH_HALF_MAX < batchCount ) {
batchArray += FirebaseFirestore.getInstance().batch()
batchCount = 0
batchIndex++
}
}
//execute all batches
if ((batchCount > 0) || (batchIndex > 0)) {
myPrintln("committing changes...")
batchArray.forEach { batch ->
batch.commit()
}
} else {
myPrintln("no records to commit")
}
myPrintln("end sync")
mFireContactHashMap.clear()
mPreferenceManager.edit().putLong(PREF_LAST_TIMESTAMP,mLastContactUpdated).apply()
mFirstRun = false
} else {
myPrintln("no change in contacts")
}
}
private suspend fun putAllUserContactsToHashMap() : Result {
var result = Result.FAILED
val batchArray = mutableListOf(FirebaseFirestore.getInstance().batch())
var batchIndex = 0
var batchCount = 0
mFireContactHashMap.clear()
var source = Source.CACHE
if (mFirstRun) {
source = Source.DEFAULT
myPrintln("get contacts via Source.DEFAULT")
} else {
myPrintln("get contacts via Source.CACHE")
}
mFireContactRef.whereEqualTo( FireContact.COLUMN_USER_ID,mUid ).get(source)
.addOnSuccessListener {documents ->
var fireContact : FireContact
for (doc in documents) {
fireContact = doc.toObject(FireContact::class.java)
if (!mFireContactHashMap.containsKey(fireContact.paPho)) {
mFireContactHashMap[fireContact.paPho] = InternalContact(fireContact.na, doc.id)
} else {
myPrintln("duplicate will be removed from firestore: ${fireContact.paPho} / ${fireContact.na} / ${doc.id}")
batchArray[batchIndex].delete(mFireContactRef.document(doc.id))
batchCount++
if (BATCH_HALF_MAX < batchCount) {
batchArray += FirebaseFirestore.getInstance().batch()
batchCount = 0
batchIndex++
}
}
}
result = Result.SUCCESS
}.addOnFailureListener { exception ->
myPrintln("Error getting documents: $exception")
}.await()
//execute all batches
if ((batchCount > 0) || (batchIndex > 0)) {
myPrintln("committing duplicate delete... ")
batchArray.forEach { batch ->
batch.commit()
}
} else {
myPrintln("no duplicates to delete")
}
return result
}

Choose specific contacts and add to application listview

I want to read all contacts and use those in my application. i read all related topics but my implementation is very ِdifferent...
i want to Achieve this form :
see Image
1-user can choose specific contacts
2- Theirs information must add in application listview
3-and user can Choose silent/Normal status for contacts that he choosen.
How i can get contact based on this scenario ?
use explicit intent and startActivityForResult() for getting contacts
static final int PICK_CONTACT_REQUEST = 1; // The request code
...
private void pickContact() {
Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers
startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
}
===============================
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request it is that we're responding to
if (requestCode == PICK_CONTACT_REQUEST) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
// Get the URI that points to the selected contact
Uri contactUri = data.getData();
// We only need the NUMBER column, because there will be only one row in the result
String[] projection = {Phone.NUMBER};
// Perform the query on the contact to get the NUMBER column
// We don't need a selection or sort order (there's only one result for the given URI)
// CAUTION: The query() method should be called from a separate thread to avoid blocking
// your app's UI thread. (For simplicity of the sample, this code doesn't do that.)
// Consider using CursorLoader to perform the query.
Cursor cursor = getContentResolver()
.query(contactUri, projection, null, null, null);
cursor.moveToFirst();
// Retrieve the phone number from the NUMBER column
int column = cursor.getColumnIndex(Phone.NUMBER);
String number = cursor.getString(column);
// Do something with the phone number...
}
}
}
Then add details to your applications listview.do as you need to
Don't know if you figure this out after a year but here's step 1:
private static final int CONTACT_PICKER_RESULT = 1001;
...
public void methodname() {
// By using ContactsContract.CommonDataKinds.Phone.CONTENT_URI, the user is
// presented with a list of contacts, with one entry per phone number.
// The selected contact is guaranteed to have a name and phone number.
Intent contactPickerIntent = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(contactPickerIntent, CONTACT_PICKER_RESULT);
return true;
}

Get Android contacts with type-to-filter functionality, restricted to a specific account

I'm trying to:
Display a list of contacts
Let the user search through them by typing a query
Limit search results only to a specific Google/Gmail account.
This is how I build the URI for the cursor:
// User is searching for 'jo'
String query = "jo";
Uri uri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(query));
// Restrict the query to contacts from 'example#gmail.com'
Uri.Builder builder = uri.buildUpon();
builder.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(ContactsContract.Directory.DEFAULT));
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, "example#gmail.com");
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.google");
uri = builder.build();
This is the final URI:
content://com.android.contacts/contacts/filter/jo?directory=0&account_name=example%40gmail.com&account_type=com.google
Currently, this shows search results from all accounts on the phone.
NOTE: If I use Contacts.CONTENT_URI instead of Contacts.CONTENT_FILTER_URI, then specifying the directory/account works as expected, but I can no longer use 'type-to-filter' style search.
The documentation does state:
The most important use case for Directories is search. A Directory
provider is expected to support at least Contacts.CONTENT_FILTER_URI.
Could anyone help point out what I might be doing wrong?
I added your code in Google's example for contact retrieving, and with a couple of changes it worked perfectly with my Google for Work account.
The changes I made were:
remove the line with DIRECTORY_PARAM_KEY, as I didn't find it to make any difference
removed ContactsQuery.SELECTION from the return statement, because that constant prevents "invisible" contacts from being displayed.
The changes were made to ContactsListFragment.java
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// If this is the loader for finding contacts in the Contacts Provider
// (the only one supported)
if (id == ContactsQuery.QUERY_ID) {
Uri contentUri;
// There are two types of searches, one which displays all contacts and
// one which filters contacts by a search query. If mSearchTerm is set
// then a search query has been entered and the latter should be used.
if (mSearchTerm == null) {
// Since there's no search string, use the content URI that searches the entire
// Contacts table
contentUri = ContactsQuery.CONTENT_URI;
} else {
// Since there's a search string, use the special content Uri that searches the
// Contacts table. The URI consists of a base Uri and the search string.
contentUri = Uri.withAppendedPath(ContactsQuery.FILTER_URI, Uri.encode(mSearchTerm));
}
// HERE COMES YOUR CODE (except the DIRECTORY_PARAM_KEY line)
Uri.Builder builder = contentUri.buildUpon();
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, "example#mycompany.com");
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.google");
contentUri = builder.build();
// Returns a new CursorLoader for querying the Contacts table. No arguments are used
// for the selection clause. The search string is either encoded onto the content URI,
// or no contacts search string is used. The other search criteria are constants. See
// the ContactsQuery interface.
return new CursorLoader(getActivity(),
contentUri,
ContactsQuery.PROJECTION,
null, // I REMOVED SELECTION HERE
null,
ContactsQuery.SORT_ORDER);
}
Log.e(TAG, "onCreateLoader - incorrect ID provided (" + id + ")");
return null;
}

Categories

Resources