Skip to content

Instantly share code, notes, and snippets.

@Sunno
Created September 2, 2015 19:08
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save Sunno/363e6811ec4b2adbc5df to your computer and use it in GitHub Desktop.
Save Sunno/363e6811ec4b2adbc5df to your computer and use it in GitHub Desktop.
Set a custom contact item in android address book
/**
* This code is based in https://github.com/nemezis/SampleContacts
* I'm just explaining it, since I lost a lot of time figuring out how it really works
* **/
package com.my_app;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Entity;
import android.database.Cursor;
import android.net.Uri;
import android.provider.Contacts;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.Settings;
import android.webkit.WebChromeClient.CustomViewCallback;
public class ContactsManager {
// this must be exactly mime type defined in contacts.xml
private static String MIMETYPE = "vnd.android.cursor.item/com.my_app.contact_item";
public static void addContact(Context context, MyContact contact) { // My Contact object is a custom object made by you
ContentResolver resolver = context.getContentResolver();
resolver.delete(RawContacts.CONTENT_URI, RawContacts.ACCOUNT_TYPE + " = ?", new String[] { AccountGeneral.ACCOUNT_TYPE });
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
/**
* this is very important, if you want to add this raw contact to a contact (please refer to android contact provider guide in order to get the difference)
* my advise is you to add contact id manually, even though most times android does this automatically.
* **/
int contact_id = 1234;
ops.add(ContentProviderOperation.newInsert(addCallerIsSyncAdapterParameter(RawContacts.CONTENT_URI, true))
.withValue(RawContacts.ACCOUNT_NAME, AccountGeneral.ACCOUNT_NAME)
.withValue(RawContacts.ACCOUNT_TYPE, AccountGeneral.ACCOUNT_TYPE)
.withValue(ContactsContract.RawContacts.CONTACT_ID, contact_id)
.build());
// this is for display name
ops.add(ContentProviderOperation.newInsert(addCallerIsSyncAdapterParameter(Settings.CONTENT_URI, true))
.withValue(RawContacts.ACCOUNT_NAME, AccountGeneral.ACCOUNT_NAME)
.withValue(RawContacts.ACCOUNT_TYPE, AccountGeneral.ACCOUNT_TYPE)
.withValue(Settings.UNGROUPED_VISIBLE, 1)
.build());
ops.add(ContentProviderOperation.newInsert(addCallerIsSyncAdapterParameter(Data.CONTENT_URI, true))
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
.withValue(StructuredName.GIVEN_NAME, contact.name)
.withValue(StructuredName.FAMILY_NAME, contact.lastName)
.build());
ops.add(ContentProviderOperation.newInsert(addCallerIsSyncAdapterParameter(Data.CONTENT_URI, true))
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, "12342145")
.build());
ops.add(ContentProviderOperation.newInsert(addCallerIsSyncAdapterParameter(Data.CONTENT_URI, true))
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Email.DATA, "sample@email.com")
.build());
//This is our custom data field in our contact
ops.add(ContentProviderOperation.newInsert(addCallerIsSyncAdapterParameter(Data.CONTENT_URI, true))
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
.withValue(Data.MIMETYPE, MIMETYPE)
.withValue(Data.DATA1, 12345)
.withValue(Data.DATA2, "sample")
.withValue(Data.DATA3, "sample")
.build());
try {
ContentProviderResult[] results = resolver.applyBatch(ContactsContract.AUTHORITY, ops);
if (results.length == 0)
;
}
catch (Exception e) {
e.printStackTrace();
}
}
private static Uri addCallerIsSyncAdapterParameter(Uri uri, boolean isSyncOperation) {
if (isSyncOperation) {
return uri.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.build();
}
return uri;
}
public static List<MyContact> getMyContacts() {
return null;
}
public static void updateMyContact(Context context, String name) {
int id = -1;
Cursor cursor = context.getContentResolver().query(Data.CONTENT_URI, new String[] { Data.RAW_CONTACT_ID, Data.DISPLAY_NAME, Data.MIMETYPE, Data.CONTACT_ID },
StructuredName.DISPLAY_NAME + "= ?",
new String[] {name}, null);
if (cursor != null && cursor.moveToFirst()) {
do {
id = cursor.getInt(0);
Log.i(cursor.getString(0));
Log.i(cursor.getString(1));
Log.i(cursor.getString(2));
Log.i(cursor.getString(3));
} while (cursor.moveToNext());
}
if (id != -1) {
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
.withValue(Data.RAW_CONTACT_ID, id)
.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
.withValue(Email.DATA, "sample")
.build());
ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
.withValue(Data.RAW_CONTACT_ID, id)
.withValue(Data.MIMETYPE, MIMETYPE)
.withValue(Data.DATA1, "profile")
.withValue(Data.DATA2, "profile")
.withValue(Data.DATA3, "profile")
.build());
try {
context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
}
catch (Exception e) {
e.printStackTrace();
}
}
else {
Log.i("id not found");
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.my_app" >
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<!-- for syncing -->
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<application
android:allowBackup="true"
android:icon="@drawable/my_icon"
android:label="@string/app_name"
android:theme="@style/AppTheme"
>
<service
android:name=".SyncService"
android:exported="true" >
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter" />
<meta-data android:name="android.provider.CONTACTS_STRUCTURE" android:resource="@xml/contacts" />
</service>
<!-- IMPORTANT: this activity is mandatory since it will not show your app icon in contact details if you ommit this
Actually omiting this made me lose a lot ot time -->
<!-- MimeType must be the same you defined in contacts.xml -->
<activity android:name=".ViewDataACtivity" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/com.my_app.contact_item" />
</intent-filter>
</activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<ContactsSource xmlns:android="http://schemas.android.com/apk/res/android">
<ContactsDataKind
android:mimeType="vnd.android.cursor.item/com.my_app.contact_item"
android:icon="@drawable/ic_launcher"
android:summaryColumn="data2"
android:detailColumn="data3"/>
</ContactsSource>
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.my_app"
android:contentAuthority="com.android.contacts" <!-- mandatory to set exactly this -->
android:userVisible="true"
android:supportsUploading="false"/>
@revant
Copy link

revant commented Mar 4, 2017

Thanks!
2017, This works! Read the comments carefully and save time!

@KennethMurugu
Copy link

Brilliant!! Absolutely brilliant. Thanks!

@aartiPl
Copy link

aartiPl commented Apr 8, 2019

Guys, correct me if I am wrong but these custom types are not saved on Google account? They are only local?
Have you verified that the custom data is saved on Google?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment