In Android, I'm unable to set a Contacts sync adapter to be read-only

139 Views Asked by At

My app syncs contacts with a custom server, with a lot of custom properties. Because of that, it has a built-in contact editor for its own contacts, and I don't want the native Contacts app to allow editing of these contacts. I thought that setting android:supportsUploading="false" in the sync-adapter xml would do what I want, but it doesn't. Here's the full XML file:

<sync-adapter
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="com.myapp.ContactSync"
    android:contentAuthority="com.android.contacts"
    android:userVisible="false"
    android:supportsUploading="false"
    android:allowParallelSyncs="false"
    android:isAlwaysSyncable="false"
    />

I've tried this on a Google Pixel 6 (Android 12), and an older tablet running Android 5.0, and the results are the same - if I open one of my "custom" contacts in the native Contacts app, it allows the user to edit it. But it's using the default set of contact fields (like Phonetic First, Middle, and Last names, etc.), which are not supported on my server. So, if the user actually enters any data into those fields, my sync adapter sees the contact as being changed, and tries to sync it back to the server.

As I said, my goal is to only allow editing of these contacts inside my app. Can that be done?

EDIT: Just to clarify, I've already eliminated the possibility of this being a some kind of a bug with Contact Aggregation. I tested this on multiple devices, where there were no other contacts loaded, only the ones added by my app. I also tried setting AGGREGATION_MODE_DISABLED for every contact,e edited, but that didn't solve the problem.

2

There are 2 best solutions below

3
marmor On

Yes, that is the correct way of doing that.

Few notes:

  1. You can use the code here to make sure your SyncAdapter was indeed recognized as a read-only SyncAdapter, and make sure the ACCOUNT_NAME and ACCOUNT_TYPE you are using in your RawContacts are correct
  2. Make sure your SyncAdapter only creates/updates its own RawContacts and not other SyncAdapters' Raws.
  3. Remember a single contact may contain multiple RawContact one from your SyncAdapter and one from some other app (e.g. Google). So when you read a modified contact you should probably only read from your own RawContact so you won't read those unsupported fields that might have been edited by the user.
0
Martin Clauß On

I stumbled across the same problem, here's what you actually asked for:

The native Contacts application decides whether a RawContact is read-only in this function: https://android.googlesource.com/platform/packages/apps/Contacts/+/refs/heads/master/src/com/android/contacts/editor/ContactEditorFragment.java#962

The relevant part is the called function areContactsWritable(): https://android.googlesource.com/platform/packages/apps/ContactsCommon/+/4e3a17eaeced54b154b76a42d050fdd9b4ac7c02/src/com/android/contacts/common/model/account/ExternalAccountType.java#238

The result depends on whether the corresponding AccountType has an EditSchema in its XML declaration: mHasEditSchema: https://android.googlesource.com/platform/packages/apps/ContactsCommon/+/4e3a17eaeced54b154b76a42d050fdd9b4ac7c02/src/com/android/contacts/common/model/account/ExternalAccountType.java#363

So in my scenario, simply removing the EditSchema from my contacts.xml (the file containing ContactsAccountType/ContactsSource) did the trick, the RawContact is now read-only. If the user tries to modify the contact (the edit button remains active) the contact editor will create a second, linked RawContact in another account (e.g. google).

The native app does NOT: evaluate if RAW_CONTACT_IS_READ_ONLY is set. Neither it evaluates: "supportsUploading" flag of the sync-adapter that may be associated with the account type

Hope that helps...