Android native Contacts application deletes every account contacts - android

My problem is that I try to disallow to native Android Contacts application to delete my application's contacts (which are specified by my application account type) from device.
For that I've specified my own SyncAdapter with it's own Service and it's meta-data described in syncadapter.xml. The value of supportsUploading is set to false (this way I say that contacts created by my application are read-only)
However, when I try to delete a contact of my application from standard Contacts app I get the message which says:
You can't delete contacts from read-only accounts, but you can hide
them in your contacts list
Everything seems fine until I try to get data of the contact which I previously deleted ("hide") from standard "Contacts" in my own application.
The returned cursor is null because there isn't any row in Data table associated with this contact's RAW_CONTACT_ID. I also check if the contact exists on the device looking for
it's DELETED flag value in RawContacts table and observed that it has been set to 1 which means that contact has been deleted.
As official documentation describes:
Some sync adapters are read-only, meaning that they only sync
server-side changes to the phone, but not the reverse. If one of those
raw contacts is marked for deletion, it will remain on the phone.
However it will be effectively invisible, because it will not be part
of any aggregate contact.
So the problem is that I can't display this contact's data in my application any more because I don't know how to retrieve them from contacts database. If somebody knows how to handle this situation I would appreciate any advice. Thanks

So after more meticulous search I found the way to retrieve data for any RAW_CONTACT in my application independently of was it deleted from some other application or not.
Using of RawContacts.Entity API does this job.
Previously I tried to retrieve contact's data using such logic:
public Cursor getContactData(long rawContactId) {
return getContentResolver().query(ContactsContract.Data.CONTENT_URI, null,
ContactsContract.Data.RAW_CONTACT_ID + "=" + rawContactId, null);
}
And this method always returned null for deleted contact.
But using RawContacts.Entity such way:
public Cursor getContactData(long rawContactId) {
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, id);
rawContactUri = Uri.withAppendedPath(rawContactUri, RawContacts.Entity.CONTENT_DIRECTORY);
return getResolver().query(rawContactUri, null, null, null);
}
allows to fetch contact's data inside application with appropriate authority regardless was it deleted by 3d-party application or not.

Related

Get cross-device unique ID for Android phone contacts

I need to query the contacts from an Android device for a project I'm working on and I need to save them in a way I can link between the instance in the app to the contact in the phonebook.
I found that the CONTACT_ID (which is a reference to _ID) of each contact might change between devices, so if I switch to other Android device that ID will not be valid.
A temp solution was using the contact's SOURCE_ID, which is a String that uniquely identifies this row to its source account. The solution was pretty good, because if the contact came from (for example) the Google account, it will stay the exact same ID on every device I'll have. The problem is - not every contact has a SOURCE_ID.
It is also possible to query a specific contact using it's data as filters, which may work as a unique ID, such as his phone number, etc... However every piece of data has a flaw. For example: A contact may have multiple phone numbers (which is still ok) and the numbers can be varied (for example: 202-555-0105 is the same as +1-202-555-0105 which is also the same as (202) 555 0105 and also 2025550105).Edit: Also not every contact has a phone number, so then what?
So after given the problem -
How can I get a unique ID for the contacts in the Android phonebook so they'll be the same cross-device?
Note: It's possible on IOS by default (see documentation) -
Contacts in different accounts that represent the same person may be automatically linked together. Linked contacts are displayed in OS X and iOS apps as unified contacts. A unified contact is an in-memory, temporary view of the set of linked contacts that are merged into one contact.
By default the Contacts framework returns unified contacts. Each fetched unified contact (CNContact) object has its own unique identifier that is different from any individual contact’s identifier in the set of linked contacts. A refetch of a unified contact should be done with its identifier.
What you are looking for is LOOKUP_KEY
An opaque value that contains hints on how to find the contact if its row id changed as a result of a sync or aggregation.
To get the contact LOOKUP_KEY loop through your contacts, here's an example:
Note: <uses-permission android:name="android.permission.READ_CONTACTS" /> is required.
val contentUri = ContactsContract.Contacts.CONTENT_URI
val cursor = context?.contentResolver?.query(contentUri, null, null, null, null)
if (cursor != null) {
while (cursor.moveToNext()) {
val id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
val name =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY))
val lookupKey =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY))
}
}
cursor?.close()
Here is how you would go about retrieving the contact with the LOOKUP_KEY:
val lookupKey = "0r1-3C263544104632"
val lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey)
val uri = ContactsContract.Contacts.lookupContact(contentResolver, lookupUri)
val cursor = context?.contentResolver?.query(uri, null, null, null, null)
if (cursor != null) {
while (cursor.moveToNext()) {
val id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
val name =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY))
}
}
cursor?.close()
}
Please note that contacts have to be synchronised in order for the LOOKUP_KEY not to be re-generated.
The LOOKUP_KEY is guaranteed to be generated for each contact, however, if the account is not set up and not synchronised then the LOOKUP_KEY will be re-generated whenever the contact gets modified.
With that in mind, you'll always have a unique LOOKUP_KEY if the device is synchronised. The LOOKUP_KEY is relying on Google Cloud which may be the same solution that Apple uses.
It will be very unlikely that an Android device will not have a google account since most Android users rely on Google services.
I am afraid this is the best way to have a unique identifier, however, if you'd like, you could hash user phone number combined with other contact details, but this method can not be guaranteed to work as contacts may change. If your users are registered and you'll have their information then you could check on the backend which hash values match your expectation and then work based on your own synchronisation.
If you want to play around, I have created a sample app with the implementation where you can look through your contacts and find the lookup key as well as retrieve the contact with the lookup key.
I would also recommend you to take a look at SyncAdapter.

Edit Call history names

I've implemented a contacts app, and I would like my application's contact names to be displayed in the device's call log history (Phone app) in case I receive/make a call to these numbers. How could I achieve that?
The CallLog.Calls table contains fields for caching names, because these are cached names, they're not expected to always be true, and are refreshed from time to time.
Usually, in most Phone/Call-log apps, when you open the call-log it'll display the calls list along with their cached names stored in the Calls table, and then spin up a background service that refreshes cached names, adding names to numbers recently saved as contacts, or updating names that had recently changed.
So if your app stored some number from the call log as a contact, if you then launch the call log app you should see the updated name appearing within a second or two.
If you want to store that name programatically in your code, you can do that easily:
String someNumber = "+12125551234";
String aName = "Jane Addams";
int numberType = Phone.TYPE_MOBILE; // see https://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.Phone#column-aliases
final ContentValues values = new ContentValues(2);
values.put(Calls.CACHED_NAME, aName);
values.put(Calls.CACHED_NUMBER_TYPE, numberType);
// on Lollipop+ device, you can also set Calls.CACHED_LOOKUP_URI and Calls.CACHED_FORMATTED_NUMBER
getContentResolver().update(Calls.CONTENT_URI, values, Calls.NUMBER + "='" + someNumber + "'", null);
Thank you #PedroHawk. I found the answer in the link you provided. More specifically, I will create an Account of my app in the Device Accounts and then use a SyncAdapter to sync the contact data from my web service to the ContactsProvider of the device.

Detect if an android contact has been deleted

I am trying to maintain a contact database and get a callback for Add/Update/Delete as soon as something changes in the URI.
I have written a ContentObserver to observe on ContactsContract.Contacts.CONTENT_URI on contacts. I get a callback as soon as a contact changes and then I update my database by checking ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP.
While this works fine for add/update, it does does not work for deleting a contact.
I do not want to parse all the contacts that I have in memory and check against android database. That would take time and CPU.
I know there exists many question of these types but I am not able to figure out things specific to deleting the contact.
Does there exist a way to perform this ?
As I have posted in above comment as well, following code works for API level 18 and above.
You can query on a uri ContactsContract.DeletedContacts.CONTENT_URI to get the list of all the contacts that have been deleted.
My query looks like following :
String selection = ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP + " > ?";
String[] selectionArgs = new String[]{String.valueOf(mLastContactDeleteTime)};
Cursor cursor = mContext.getContentResolver().query(ContactsContract.DeletedContacts.CONTENT_URI, null, selection, selectionArgs, null);

How to retrieve the number of new contacts added to the contacts database

is there a column in the database of contacts indicates the date added to the base or the last modification date, because I want a method used to retrieve the new add
I found SEVERAL method but I did not understand, I am new to android
Edit : Means how to get the newly updated contacts.
you want to retrieve the number of new contacts since when?
if you adding contacts with your app then just get the number of contacts before you add any and save that value.
then after the user has added contacts get the number again and find the difference between the previous number of contacts. that will be the number of new contacts, but the best way to do it would just be to keep an internal counter of when the user adds contacts.
I think you are looking for something like this, You want to get the updated contacts.
have look here....
Contacts ContentObserver called randomly
Android notify when phone book is updated(Content Observer)
ContentObserver for listening contact changes
Just make this change to your cursor query
Cursor cursor = getActivity().getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, "_id DESC");

Picking the correct rawContactID when adding a new number to existing contact

I am building a custom phonebook application. As part of this, I need to add new contacts, or additional info (phone numbers or emails) under existing contacts. While this works well under simple test cases, there are certain situations (and it's not obvious when) where I end up with two issues:
Multiple copies of a contact get created, often hundreds! These are all blank, and simply share the same display name with the original contact that was modified
Multiple numbers (exactly the same) under the contact. The really unusual thing is that these numbers have the same _ID in the contactContracts.Data table, so can't understand how there can be multiple instances with the same unique ID. I checked to see if it's a display issue, but doesn't seem to be
Both of the above makes me question if I am inserting information correctly. So what I want to know is:
When creating a new contact, is there a default account_name and account_type I should be specifying? I am currently using null for both
When I add a new phone number to an existing contact, what is the right approach to inserting data into the contactContracts.Data table? I am currently inserting this number with rawContactID as a reference ID, and I am getting this rawContactID from the contactContracts.RawContacts table, by using my contact's contactID as reference. Note that because I often get multiple raw contacts, I end up picking up the last one (arbitrary choice) to do my phone number insert.
You can use AccountManager Class for getting account name & account type information.
final AccountManager accountTypes = AccountManager.get(getApplicationContext());
final Account accounts[] = accountTypes.getAccounts();
for (final Account account: accounts) {
Log.i(TAG, account.name);
}

Categories

Resources