I am trying to delete a contact from phone contacts. The contact gets deleted from phone contacts but it's not getting deleted from the server-side (Google contacts) and when the Google contact sync triggers then that deleted contact re-appears. Below is my code.
public static void deleteContact(long rawid, ContentResolver contentResolver) {
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
Uri uri = ContactsContract.RawContacts.CONTENT_URI
.buildUpon()
.appendQueryParameter(
ContactsContract.CALLER_IS_SYNCADAPTER,
"true")
.build();
ops.add(ContentProviderOperation
.newDelete(uri)
.withSelection(
ContactsContract.RawContacts._ID + " = ?",
new String[]{Long.toString(rawid)})
.build());
try {
contentResolver.applyBatch(
ContactsContract.AUTHORITY,
ops);
} catch (RemoteException | OperationApplicationException e) {
e.printStackTrace();
}
}
You should try with ContactsContract.CALLER_IS_SYNCADAPTER as false in your code. While set to true, the contact is permanently deleted from the database. But when the next sync happens the contact is synched back. How Google sync checks for deleted contacts, is using a deleted flag which is set only if you set ContactsContract.CALLER_IS_SYNCADAPTER as false. Below is a snippet of code from the ContactsProvider class (contentprovider for contacts datastore)
if (callerIsSyncAdapter || rawContactIsLocal(rawContactId)) {
// When a raw contact is deleted, a SQLite trigger deletes the parent contact.
// TODO: all contact deletes was consolidated into ContactTableUtil but this one can't
// because it's in a trigger. Consider removing trigger and replacing with java code.
// This has to happen before the raw contact is deleted since it relies on the number
// of raw contacts.
db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null);
count = db.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null);
mTransactionContext.get().markRawContactChangedOrDeletedOrInserted(rawContactId);
} else {
count = markRawContactAsDeleted(db, rawContactId, callerIsSyncAdapter);
}
Related
So I've written a query to extract the WhatsApp contacts of a phone. My initial query goes like this:
Cursor c = con.getContentResolver().query(
ContactsContract.RawContacts.CONTENT_URI,
new String[]{ContactsContract.RawContacts.CONTACT_ID, ContactsContract.RawContacts.DISPLAY_NAME_PRIMARY},
ContactsContract.RawContacts.ACCOUNT_TYPE + "= ?",
new String[]{"com.whatsapp"},
null
);
ArrayList<String> myWhatsappContacts = new ArrayList<String>();
int contactNameColumn = c.getColumnIndex(ContactsContract.RawContacts.DISPLAY_NAME_PRIMARY);
while (c.moveToNext()) {
// You can also read RawContacts.CONTACT_ID to read the
// ContactsContract.Contacts table or any of the other related ones.
myWhatsappContacts.add(c.getString(contactNameColumn));
}
The purpose of this is to find out how many WhatsApp contacts the phone has at any one time. When I do:
Log.i("WhatsApp contacts found:", Integer.toString(myWhatsappContacts.size());
It should print out how many WhatsApp contacts there were into LogCat. And this works - up to a point.
Let's say for example that the number of WhatsApp contacts I have now is 101. The next phase of this little project is to delete away ALL contacts if there are more than 100 of them. In which case, we go:
if (myWhatsappContacts.size() > 100) {
//Delete all contacts code here
}
I've tested the delete contacts code, it works. I check the contacts directory of the phone via the contacts app, and it says 0. But now when I do the query again (refer to code above), it still shows 101! What's going on?
If it helps, my DeleteContacts method is as follows:
private void deleteContact(Context ctx, String phone, String name) {
Uri contactUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phone));
Cursor cur = ctx.getContentResolver().query(contactUri, null, null, null, null);
try {
if (cur.moveToFirst()) {
do {
if (cur.getString(cur.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME)).equalsIgnoreCase(name)) {
String lookupKey = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey);
ctx.getContentResolver().delete(uri, null, null);
return;
}
} while (cur.moveToNext());
}
} catch (Exception e) {
System.out.println(e.getStackTrace());
} finally {
cur.close();
}
return;
}
What am I doing wrong? Is my DeleteContacts code faulty? Or is the query itself faulty?
private void displayAllContactsByType(String accountName) {
Cursor rawCursor = null;
try {
rawCursor = mCProviderClient.query(
ContactsContract.RawContacts.CONTENT_URI,
null,
ContactsContract.RawContacts.ACCOUNT_NAME + "= ?",
new String[]{accountName},
null);
} catch (RemoteException e) {
e.printStackTrace();
}
int contactIdColumn = rawCursor.getColumnIndex(ContactsContract.RawContacts.CONTACT_ID);
Utils.Log("Raw Size", " " + rawCursor.getCount());
while (rawCursor.moveToNext()) {
String contactId = rawCursor.getString(contactIdColumn);
// Utils.Log("contactId ",contactId);
storeContactDetails(contactId);
}
if (contactList != null) {
contactListener = (ContactListener) context;
contactListener.updateView(contactList);
}
}
I am getting all the account name which raw contacts contain. And I would like to get all the contacts belongs to the particular account name but I am getting null contact id. How can I get all the contact id to a particular account name and then respective data of that contact id along with it?
First, RawContacts.CONTACT_ID is of type Long, not String, so you should do:
Long contactId = rawCursor.getLong(contactIdColumn);
See: https://developer.android.com/reference/android/provider/ContactsContract.RawContactsColumns.html#CONTACT_ID don't pay attention to the type of the CONTACT_ID field which are always Strings, look at Type: INTEGER below it.
Second, this is rare, but can happen when a RawContact does not belong to any Contact, I call those zombie RawContacts, they can be created either via a corrupt DB or a bug, or intentionally using AGGREGATION_MODE when creating the RawContact, see AGGREGATION_MODE_DISABLED
I've been trying to make multi contact picker list with checkbox. I already created Multi Contact Picker with contact having atleast one phone number in its contact.
Que
Now I've added contact with in ListView. But contact appear with single contact number in it, it's taking first phone number which is saved in Contact Application. I've added LinkedHashMap & Set to add indexer on the Contact List.
Did LinkedHashMap or Set is removing duplicate values from Collections?
How can i fetch all the phone number associated with that contact name?
Code Snippet:-
ContentResolver cr = getContentResolver();
String selection = Data.HAS_PHONE_NUMBER + " > '" + ("0") + "'";
Cursor cur = cr.query(Data.CONTENT_URI, new String[] { Data.CONTACT_ID, Data.MIMETYPE, Email.ADDRESS,
Contacts.DISPLAY_NAME, Phone.NUMBER }, selection, null, Contacts.DISPLAY_NAME);
Contact contact;
if (cur.getCount() > 0) {
while (cur.moveToNext()) {
String id = cur.getString(cur.getColumnIndex(Data.CONTACT_ID));
String mimeType = cur.getString(cur.getColumnIndex(Data.MIMETYPE));
if (allContacts.containsKey(id)) {
// update contact
contact = allContacts.get(id);
} else {
contact = new Contact();
allContacts.put(id, contact);
// set photoUri
contact.setContactPhotoUri(getContactPhotoUri(Long.parseLong(id)));
}
if (mimeType.equals(StructuredName.CONTENT_ITEM_TYPE))
// set name
contact.setContactName(cur.getString(cur.getColumnIndex(Contacts.DISPLAY_NAME)));
if (mimeType.equals(Phone.CONTENT_ITEM_TYPE))
// set phone munber
contact.setContactNumber(cur.getString(cur.getColumnIndex(Phone.NUMBER)));
}
}
cur.close();
// get contacts from hashmap
contacts.clear();
contacts.addAll(allContacts.values());
These is just a temporary fix as of now, I've been trying to add all contact number associated with same contact person inside android.provider.ContactsContract.
I've added all contact number associated with same contact person inside beans by appending with delimiter(",") which i can fetch back using Tokenizer. But no idea to know which kinda contact type it is whether HOME, WORK, OFFICE , it's less important to me as of now.
Fix:
if (mimeType.equals(Phone.CONTENT_ITEM_TYPE)){
// set phone number
if (contact.getContactNumber().toString().length() == 0) {
contact.setContactNumber(cur.getString(cur.getColumnIndex(Phone.NUMBER)).replaceAll("\\D", ""));
} else {
contact.setContactNumber(contact.getContactNumber().toString().concat(", ").concat(cur.getString(cur.getColumnIndex(Phone.NUMBER)).replaceAll("\\D", "")));//One can add possible contacts "(-/,"
}
}
I'm changing the name of a published app.
Is there a quick and safe way to change the account name created via AccountManager.addAccountExplicitly so that existing info will remain intact for existing users.
If not, how can I go about changing the account name manually while preserving all the data?
I'll post an answer of my naive approach of copying everything then deleting the old, but I'm sure someone will come up with a better one (or spot some bugs in my method).
API v21 added a renameAccount() method to the AccountManager, if that helps.
From the docs:
This is equivalent to removing the existing account and adding a new
renamed account with the old account's user data.
That means for backward compatibility, you would have to manually remove the account and run through the same procedure as creating a new one (AccountManager.addAccountExplicitly() and AccountManager.setUserData()) afterwards.
Edit:
If you want to update your contacts afterwards to display the correct account name, try this (untested) code:
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.RawContacts.ACCOUNT_NAME, "new account name");
getContext().getContentResolver().update(ContactsContract.RawContacts.CONTENT_URI,
contentValues,
ContactsContract.RawContacts.ACCOUNT_TYPE + " = ? AND " + ContactsContract.RawContacts.ACCOUNT_NAME + " = ?",
new String[]{"your account type", "old account name"});
A naive approach of going over all the records, copying them one by one, and deleting all the old stuff...
I'm really afraid this method might fail on real world users.
private void naiveRename(ContentResolver resolver) {
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
Cursor cur = resolver.query(RawContacts.CONTENT_URI, null, RawContacts.ACCOUNT_NAME + "='"
+ "OLD NAME" + "'", null, null);
if (cur != null) {
// copy all data
while (cur.moveToNext()) {
Uri curUri = RawContacts.CONTENT_URI.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.build();
ContentProviderOperation.Builder builder = ContentProviderOperation
.newInsert(curUri);
for (int i = 0; i < cur.getColumnCount(); i++) {
String colName = cur.getColumnName(i);
if (RawContacts._ID.equals(colName) || RawContacts.VERSION.equals(colName)
|| RawContacts.CONTACT_ID.equals(colName)) {
// Skip - read only
} else if (RawContacts.ACCOUNT_NAME.equals(colName)) {
builder.withValue(RawContacts.ACCOUNT_NAME, "NEW NAME");
} else {
builder.withValue(colName, cur.getString(i));
}
}
operationList.add(builder.build());
}
// delete all old data
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(RawContacts.CONTENT_URI);
builder.withSelection(RawContacts.ACCOUNT_NAME + "='" + "OLD NAME" + "'", null);
try {
resolver.applyBatch(ContactsContract.AUTHORITY, operationList);
} catch (RemoteException e) {
// PANIC!
} catch (OperationApplicationException e) {
// OMG! WHAT TO DO?!
}
} else {
// LORDI!
}
}
I am working on Unit Tests for my Android app, and am doing a lot with Contacts. I have to insert contacts into the Android Content Providers, and delete them after running my tests. Trouble is, they do not get actually deleted:
Insertion:
ArrayList<ContentProviderOperation> contactOps = new ArrayList<ContentProviderOperation>();
int backRefIndex = 0;
Random r = new Random();
contactOps.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
.build());
contactOps.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, backRefIndex)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, "Sample Name" + r.nextInt())
.build());
contactOps.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, backRefIndex)
.withValue(ContactsContract.CommonDataKinds.Phone.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, "020" + r.nextInt())
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, r.nextInt(20)
.build());
try {
ContentProviderResult[] result = context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, contactOps);
} catch (Exception e) {
e.printStackTrace();
}
Deletion method 1 (returns number of raw contacts, but they do not actually get deleted):
int deletedRawContacts = context.getContentResolver().delete(ContactsContract.RawContacts.CONTENT_URI, ContactsContract.RawContacts._ID + " >= ?", new String[]{"0"});
Deletion method 2 (same result as deletion method 1, but different approach):
private static int deleteAllRawContacts(Context context) {
ContentResolver cr = context.getContentResolver();
Cursor cur = cr.query(ContactsContract.RawContacts.CONTENT_URI, null, null, null, null);
int count = 0;
while (cur.moveToNext()) {
try {
String contactId = cur.getString(cur.getColumnIndex(ContactsContract.RawContacts._ID));
count += cr.delete(ContactsContract.RawContacts.CONTENT_URI, ContactsContract.RawContacts._ID + " = ?", new String[]{contactId});
} catch (Exception e) {
System.out.println(e.getStackTrace());
}
}
return count;
}
The deletion method for Contacts works, but the deletion method for Raw Contacts will return a false value. It will "tell" me, that it deleted all contacts, but when I run my next test case, the old Raw Contacts can still be found (i.e. the count of inserted contacts vs. present contacts is wrong). Note: All testing is done on the Android emulator.
Any ideas how to solve this?
I saw a similar question here: How to delete a contact? - but the solution does not seem to solve the given problem either.
As wiseideal already mentioned the way you delete your rawcontacts will only set the "deleted"-flag to 1.
What you need to do is to set the caller_is_syncadapter-flag in your URI to true like this:
RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build()
And then use this new URI to call the delete-method:
int deletedRawContacts = context.getContentResolver().delete(RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").build(), ContactsContract.RawContacts._ID + " >= ?", new String[]{"0"});
The corresponding part in the documentation is here (Operations->delete).
Hope this helps and happy coding :)
I am working on the same issue.I found the delete column is setted to 1 once I "delete" it.So I think contentresolver doesnt delete the rawcontact data physically,it just set a delete flag.Maybe we should avoid these tag.