I am trying to fetch deleted contacts from Android contacts table. After searching some Stack Overflow links, best way to fetch the deleted contacts is by using ContactsContract.DeletedContacts. I am able to fetch the deleted contacts from DeletedContacts table.
val deletedContactsProjection = arrayOf(ContactsContract.DeletedContacts.CONTACT_ID,ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP)
val contactLastDeletedTimeStamp = SharedPreferenceManager.instance.getLongFromPreference("contact_sync_last_updated_timestamp")
val deletedContactSelection = ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP + " > ?";
val deletedContactSelectionArgs = arrayOf<String>(contactLastDeletedTimeStamp.toString())
var deletedContactsCursor = context.contentResolver.query(ContactsContract.DeletedContacts.CONTENT_URI, deletedContactsProjection,deletedContactSelection, deletedContactSelectionArgs,null)
Using the above cursor I'm able to fetch the last deleted contact. When I checked the ContactsContract.DeletedContacts.CONTACT_ID of the deleted contact from cursor, it's same as the contact id which I deleted. But the problem is when I'm trying to fetch the contact from ContactsContract.Contacts table using the ContactsContract.DeletedContacts.CONTACT_ID its always returning null.
This is the query I'm using:
String[] contactNumberProjection = {ContactsContract.Contacts._ID};
String contactId = deletedContactsCursor.getString(deletedContactsCursor.getColumnIndex(ContactsContract.DeletedContacts.CONTACT_ID));
String contactSelection = ContactsContract.Contacts._ID + " = ?";
String[] contactSelectionArg = {contactId};
Cursor contactCursor =
context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,contactNumberProjection,contactSelection,contactSelectionArg,null);
Where am I going wrong?
When a contact is deleted from the main Contacts table it is obviously removed from that table so that the contact will not appear in the Contacts apps the user is using the view their present contacts.
So you can only get details of deleted contacts in the DeletedContacts table.
Related
I am creating an android app that keeps track of all contacts saved on the device (the ones that appear in the default android contacts app) and saves them in a MySQL table on the server.
I managed to read all contact data using ContectResolver:
//to read only android address book
String where = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '1'";
String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY, ContactsContract.Contacts.HAS_PHONE_NUMBER};
ContentResolver cr = context.getContentResolver();
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, projection, where, null, null);
Then, using the ContactsContract.Contacts._ID I query additional contact data:
// Perform a query to retrieve the contact's name parts
String[] nameProjection = new String[] {
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME,
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME
};
Cursor nameCursor = cr.query(
ContactsContract.Data.CONTENT_URI,
nameProjection,
ContactsContract.Data.MIMETYPE + " = '" +
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "' AND " +
ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID
+ " = ?", new String[] { id }, null);
// Retrieve the name parts
String firstName = "", middleName = "", lastName = "", displayName = "";
if(nameCursor.moveToNext()) {
firstName = nameCursor.getString(nameCursor.getColumnIndex(
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
middleName = nameCursor.getString(nameCursor.getColumnIndex(
ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME));
lastName = nameCursor.getString(nameCursor.getColumnIndex(
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
displayName = nameCursor.getString(nameCursor.getColumnIndex(
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME));
}
I then send all this data (including the contact's email addresses and phone numbers) to the server.
I want to be able to periodically backup the device's contacts like this, but to be able to detect changes in contact data, by comparing the old data to the new data. But to do this, i need to have some sort of link between the contacts in the android device, and my DB in which i save the contact data.
After some searching i found 3 options:
ContactsContract.Contacts._ID
ContactsContract.Contacts.LOOKUP_KEY
ContactsContract.RawContacts._ID
But from what I found, all of them are not reliable, and can change in certain situations - invalidating the link between my DB to the device's contact list.
I have considered the option to use ContentObserver to detect contact changes. But, I want to be able to detect contact changes even if my app has been uninstalled, then some contacts have changed and then my app has been reinstalled.
Is there a reliable identification key per contact that I can use, to know when a certain contact has been changed or deleted?
EDIT:
I now found this variable exists: ContactsContract.ContactsColumns.CONTACT_LAST_UPDATED_TIMESTAMP
The problem is that it was introduces only in API level 18. I am working on min API 15. Is there something that could replace it for the missing API levels?
I am afraid it is not possible to know if particular contact changed. Therefore you can register observer on whole address book URI. This way you will be able to listen when anything in address book changes.
Now having observer you can scan all contacts and perform "sync" data to your server.
In your particular case pair of ContactsContract.Contacts._ID and ContactsContract.Contacts.LOOKUP_KEY should be reliable enough. But in order to know if anything changed you need either:
send all contacts to server and perform sync logic on server (id and lookup key should be stored on MySQL)
keep local copy of contacts on your device (sqllite db, realm, any other storage) in order to avoid unchanged contacts to be sent to server every time you sync
I want to insert a new phone number with new contact id in the my SQLite. I need to know the maximum contact id in my contact, to ignore the saving same contact id. Is it possible to know the maximum contact id in Android? Thank you
From Contacts Table , you can get the last Contact Id by querying like shown below
final String sortOrder = ContactsContract.Contacts._ID+" DESC LIMIT 1";
Cursor lcursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,new String[] {ContactsContract.Contacts._ID,ContactsContract.Contacts.DISPLAY_NAME},null,null,sortOrder);
if (lcursor != null && lcursor.moveToFirst()) {
long id = lcursor.getLong(lcursor.getColumnIndex(ContactsContract.Contacts._ID));
String displayName = lcursor.getString(lcursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
}
Cursor will return only one entry and that entry will be the last contact_id in the contact's table
Note: This should not be done on Main thread, as it may take too much time if device Contact is more 1000+ contacts, so always be carefull of Querying the DataBase having large entries
Enjoy coding ! :)
i want to fetch the contacts from android periodically. for that i
want to something like this. i am taking all the conatcts from the
android and store it in my local db. so i will store last contact id.
so next time on wards if any contacts added , i need to get those
contacts only so for that purpose i want to fetch the contact id
greater than last time fetched contact id. how can write the query for
content resolver to fetch the contacts some thing like.
Select * from tablename where id > 100;
Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
null, // this will be the columns selection
ContactsContract.Contacts._ID + ">100", // this is where you add the "where" part of the query, id is 1 in this case
null, // where args, you are skipping these
null); // sort order
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
long id = Long.parseLong(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)));
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)).trim();
}
}
Hope this solves your issue
I'm trying to get "notes" from a single contact. It added fine but retrieving it has been a problem.
String selection = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME+" like'%" + sender +"%'";
String[] projection = new String[] { ContactsContract.CommonDataKinds.Note.NOTE};
Cursor c2 = getContentResolver().query(ContactsContract.Data.CONTENT_URI, projection, selection, null, null);
if (c2.moveToFirst()) {
notes = c2.getString(0);
}
It works fine with other values like name or phone number but can't seem to get notes to retrieve correctly. It retrieves a random value like email instead.
I believe that your problem is that not all rows in the table represent contact types that have notes. You have to request the proper MIME Type.
ContactsContract.CommonDataKinds.Note is an alias for the 'data1' column that is present on all rows, so when you get a row of a different MIME Type, it represents different data.
How to get contacts in Android should give you an idea of how to do this.
I query the CallLog.Calls provider in order to retrieve a list of calls from a certain contact, based on the contact's display name. In particular, I use this query:
String selection = CallLog.Calls.CACHED_NAME + "= ?";
String dispName = dataCollector.getDisplayName();
Cursor callCursor =
cr.query(callLogUri, callLogProjection, selection,
new String[] {dispName},CallLog.Calls.DATE + " DESC");
The dataCollector object is used to hold information from queries based on a given contact id.
The problem is that this code only returns one call for the given contact. I can't understand why. Any clues?
int i=0;
while(cursor.moveToNext())
{
Sring id = cursor.getString(cursor.getColumnIndex(CallLog.Calls._ID));
numbersTemp[i]=cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
valuesTemp[i]=cursor.getString(cursor.getColumnIndex(CallLog.Calls.CACHED_NAME));
i++;
}