Contacts Provider

Contacts Provider

Contacts Provider Organization
Contacts Provider table structure

Contacts --> people, Raw --> a summary of people, Data --> the detail of raw contacts
other auxiliary table

Raw contacts
  • m x raw <--> a single acount type <--> multi-sync-service
Notes
  • Raw conteacts name in Data
  • Cauion: To use your own account data in a raw contact row.

register in Account Manager
sample_01 maintain contacts data for your web-based servic
service url :com.example.dataservice
usr's account: becky.sharp@dataservice.example.com
the user must first add the account "type" (com.example.dataservice) and account "name" (becky.smart@dataservice.example.com) before your app can add raw contact rows.

Sources of raw contacts data

Suppose Emily Dickinson opens a browser window, logs into Gmail as emily.dickinson@gmail.com, opens Contacts, and adds "Thomas Higginson". Later on, she logs into Gmail as emilyd@gmail.com and sends an email to "Thomas Higginson", which automatically adds him as a contact. She also follows "colonel_tom" (Thomas Higginson's Twitter ID) on Twitter. ==> The Contacts Provider creates three raw contacts as a result of this work:

Data

the data for a raw contact is stored in a ContactsContract.Data row that is linked to the raw contact's _ID value.

  • Column names
    raw_contact_id
    MIMETYPE -->> define ContactsContract.CommonDataKinds
    IS_PRIMARY if the user long-presses a phone number for a contact and selects Set default --> !zero
  • Generic column names 1~15
    DATA1 --> index
    DATE15 --> Binary Larg Object (BLOB) such as photo thumbnails
Type-specific column names

Caution: add your own custom data MIMETYPE in data table must ContactsContract.CommonDataKinds ++

Contacts

The CONTACT_ID column of theraw contacts table ContactsContract.RawContacts contains _ID values for the contacts row associated with each raw contacts row.

  • The column LOOKUP_KEY that is a "permanent" link to the contact row
  • _ID column may change

Data From Sync Adapters

Users enter contacts data directly into the device, but data also flows into the Contacts Provider from web services via sync adapters, which automate the transfer of data between the device and services. Sync adapters run in the background under the control of the system, and they call ContentResolver methods to manage data.

ContactsDataFlow

The User Profile

This data describes the device's user. access to the user profile requires the READ_PROFILE and WRITE_PROFILE permissions.

// Sets the columns to retrieve for the user profile
mProjection = new String[]{
        Profile._ID,
        Profile.DISPLAY_NAME_PRIMARY,
        Profile.LOOKUP_KEY,
        Profile.PHOTO_THUMBNAIL_URI
    };
// Retrieves the profile from the Contacts Provider
mProfileCursor =getContentResolver().query(
                Profile.CONTENT_URI,
                mProjection ,
                null,
                null,
                null);

Contacts Provider Metadata

Column value Meaning
RawContacts/DIRTY 0/1(need sync to service)
RawContacts/VERSION its related data changes ++
Data/DATA_VERSION its related data changes ++
RawContacts/SOURCE_ID ==service database reflect row id
Groups 0/1 visible or invsible in application UIs
Settings/UNGROUPED_VISIBLE 0/1 don't belong to a group v/invsible
SyncState (all) metadata

Contacts Provider Access

Querying entities

For example, to display all the information for a person, you may want to retrieve all the ContactsContract.RawContacts rows for a single ContactsContract.Contacts row, or all the ContactsContract.CommonDataKinds.Email rows for a single ContactsContract.RawContacts row. To facilitate this, the Contacts Provider offers entity constructs, which actlike database joins between tables.

Note: An entity usually doesn't contain all the columns of the parent and child table. If you attempt to work with a column name that isn't in the list of column name constants for the entity, you'll get an Exception.

snippet

...
    /*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    mContactUri = Uri.withAppendedPath(
            mContactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);

    // Initializes the loader identified by LOADER_ID.
    getLoaderManager().initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this);      // The context of the activity

    // Creates a new cursor adapter to attach to the list view
    mCursorAdapter = new SimpleCursorAdapter(
            this,     // the context of the activity
            R.layout.detail_list_item,   
            mCursor,  // the backing cursor
            mFromColumns,   // the columns in the cursor that provide the data
            mToViews,  // the views in the view item that display the data
            0);                          // flags

    // Sets the ListView's backing adapter.
    mRawContactList.setAdapter(mCursorAdapter);
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    /*
     * Sets the columns to retrieve. RAW_CONTACT_ID is included 
     * to identify the raw contact associated with the data row.
     * DATA1 contains the first column in the data row (usually the most important one).
     * MIMETYPE indicates the type of data in the data row.
     */
    String[] projection ={
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
        };

    /*
     * Sorts the retrieved cursor by raw contact id, to keep all data
     * rows for a single raw contact collated together.
     */
    String sortOrder =
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
            " ASC";
    /*
     * Returns a new CursorLoader. The arguments are similar to
     * ContentResolver.query(), except for the Context argument, which 
     * supplies the location of the ContentResolver to use.
     */
    return new CursorLoader(
            getApplicationContext(),  // The activity's context
            mContactUri,       // The entity content URI for a single contact
            projection,               // The columns to retrieve
            null,  // Retrieve all the raw contacts and their data rows.
            null,                     //
            sortOrder); // Sort by the raw contact ID.
}
Batch modification

Note: To modify a single raw contact, consider sending an intent to the device's contacts application rather than handling the modification in your app.

Yield points

A batch modification containing a large number of operations can block other processes, resulting in a bad overall user experience. A yield point is a ContentProviderOperation object that has its isYieldAllowed() value set to true. When the Contacts Provider encounters a yield point, it pauses its work to let other processes run and closes the current transaction. When the provider starts again, it continues with the next operation in the ArrayList and starts a new transaction.

Modification back references

insert a new raw contact row must insert contact's _ID to data RAW_CONTACT_ID ==>>
ContentProviderOperation.Builder.withValueBackReference().
key ==> a column in the table that you're modifying.
previousResult ==> The 0-based index of a value in the array of ContentProviderResult objects from applyBatch().
snippet-02

   // Inserts the specified email and type as a Phone data row
    op =ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);
    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true);
    // Builds the operation and adds it to the array of operations
    ops.add(op.build());
     try {
            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
    } catch (Exception e) {
            // Display a warning
            Context ctx = getApplicationContext();
            CharSequence txt = getString(R.string.contactCreationFailure);
            int duration = Toast.LENGTH_SHORT;
            Toast toast = Toast.makeText(ctx, txt, duration);
            toast.show();
            // Log exception
            Log.e(TAG, "Exception encountered while inserting contact: " + e);
    }
}
optimistic concurrency control ??
  • Retrieve the raw contact's VERSION column along with the other data you retrieve.
  • Create a ContentProviderOperation.Builder object suitable for enforcing a constraint, using the method newAssertQuery(Uri). For the content URI, use RawContacts.CONTENT_URI with the raw contact's _ID appended to it.
  • For the ContentProviderOperation.Builder object, call withValue() to compare the VERSION column to the version number you just retrieved.
  • For the same ContentProviderOperation.Builder, call withExpectedCount() to ensure that only one row is tested by this assertion.
  • Call build() to create the ContentProviderOperation object, then add this object as the first object in the ArrayList that you pass to applyBatch().
  • Apply the batch transaction.

snippet-03 -- Create an "assert" ContentProviderOperation after querying for a single raw contact using a CursorLoader:

 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
    // Gets the raw contact's _ID and VERSION values
    mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}
...
// Sets up a Uri for the assert operation
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);
// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);
// Adds the assertions to the assert operation: checks the version and count of rows tested
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);
// Creates an ArrayList to hold the ContentProviderOperation objects
ArrayList ops = new ArrayList<ContentProviderOperationg>;
ops.add(assertOp.build());
// You would add the rest of your batch operations to "ops" here
// Applies the batch. If the assert fails, an Exception is thrown
try {
        ContentProviderResult[] results =getContentResolver().applyBatch(AUTHORITY, ops);
} catch (OperationApplicationException e) {
        // Actions you want to take if the assert operation fails go here
}
Retrieval and modification with intents
Contacts Provider intents
Action Data MIME type Notes
ACTION_PICK Contacts.CONTENT_URI Phone.CONTENT_URI StructuredPostal.CONTENT_URI,Email.CONTENT_URI
Insert.ACTION
ACTION_EDIT
ACTION_INSERT_OR_EDIT

The device's contacts app doesn't allow you to delete a raw contact or any of its data with an intent. Instead, to delete a raw contact, use ContentResolver.delete() or ContentProviderOperation.newDelete().

snippet-04

// Creates a new intent for sending to the device's contacts application
Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);

// Sets the MIME type to the one expected by the insertion activity
insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);

// Sets the new contact name
insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);

// Sets the new company and job title
insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);

/*
* Demonstrates adding data rows as an array list associated with the DATA key
*/

// Defines an array list to contain the ContentValues objects for each row
ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();

/*
* Defines the raw contact row
*/

// Sets up the row as a ContentValues object
ContentValues rawContactRow = new ContentValues();

// Adds the account type and name to the row
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());

// Adds the row to the array
contactData.add(rawContactRow);

/*
* Sets up the phone number data row
*/

// Sets up the row as a ContentValues object
ContentValues phoneRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
phoneRow.put(
       ContactsContract.Data.MIMETYPE,
       ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
);

// Adds the phone number and its type to the row
phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);

// Adds the row to the array
contactData.add(phoneRow);

/*
* Sets up the email data row
*/

// Sets up the row as a ContentValues object
ContentValues emailRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
emailRow.put(
       ContactsContract.Data.MIMETYPE,
       ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
);

// Adds the email address and its type to the row
emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);

// Adds the row to the array
contactData.add(emailRow);

/*
* Adds the array to the intent's extras. It must be a parcelable object in order to
* travel between processes. The device's contacts app expects its key to be
* Intents.Insert.DATA
*/
insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent);
Data integrity
  • Always add a ContactsContract.CommonDataKinds.StructuredName row for every ContactsContract.RawContacts row you add.

A ContactsContract.RawContacts row without a ContactsContract.CommonDataKinds.StructuredName row in the ContactsContract.Data table may cause problems during aggregation.

  • Always link new ContactsContract.Data rows to their parent ContactsContract.RawContacts row.

A ContactsContract.Data row that isn't linked to a ContactsContract.RawContacts won't be visible in the device's contacts application, and it might cause problems with sync adapters.

  • Change data only for those raw contacts that you own.

Custom data rows

By creating and using your own custom MIME types, you can insert, edit, delete, and retrieve your own data rows in the ContactsContract.Data table. Your rows are limited to using the column defined in ContactsContract.DataColumns, although you can map your own type-specific column names to the default column names. In the device's contacts application, the data for your rows is displayed but can't be edited or deleted, and users can't add additional data. To allow users to modify your custom data rows, you must provide an editor activity in your own application.

To display your custom data, provide a contacts.xml file containing a <ContactsAccountType> element and one or more of its <ContactsDataKind> child elements. This is described in more detail in the section <ContactsDataKind> element.

Contacts Provider Sync Adapters

The Contacts Provider is specifically designed for handling synchronization of contacts data between a device and an online service.

the Android system provides a plug-in synchronization framework that automates the following tasks:

  • Checking network availability.
  • Scheduling and executing synchronization, based on user preferences.
  • Restarting synchronizations that have stopped.
Sync adapter classes and files

implement AbstractThreadSyncAdapter --> system read manifest Xml file --> user add an acount for the sync
--> system starts managing the adapter.

Note: Using an account type as part of the sync adapter's identification allows the system to detect and group together sync adapters that access different services from the same organization. For example, sync adapters for Google online services all have the same account type com.google. When users add a Google account to their devices, all of the installed sync adapters for Google services are listed together; each sync adapter listed syncs with a different content provider on the device.

  • the Android system offers an authentication framework AbstractAccountAuthenticator
    If the service accepts the credentials, the authenticator can store the credentials for later use. Because of the plug-in authenticator framework, the AccountManager can provide access to any authtokens an authenticator supports and chooses to expose, such as OAuth2 authtokens.
Social Stream Data

ContactsContract.StreamItems and ContactsContract.StreamItemPhotos tables manage incoming data from social networks

  • Social stream text
  • Social stream photos
Social stream interactions
  • Regular synchronization
  • Trigger synch youself
  • Trigger when need by registering a notification
Registering to handle social networking views

res/xml/contacts.xml

<ContactsAccountType
        xmlns:android="http://schemas.android.com/apk/res/android"
        inviteContactActivity="activity_name"
        inviteContactActionLabel="invite_command_text"
        viewContactNotifyService="view_notify_service"
        viewGroupActivity="group_view_activity"
        viewGroupActionLabel="group_action_text"
        viewStreamItemActivity="viewstream_activity_name"
        viewStreamItemPhotoActivity="viewphotostream_activity_name">
Additional Contacts Provider Features
  • Contact groups
  • Contact photos
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • “其实你我都一样,终将被遗忘,郭源潮。 你的病也和我的一样,风月难扯,离合不骚。 层楼终究误少...
    南云渊阅读 196评论 0 4
  • 1、我想以后从事农业,现在应该怎么准备 答:先成为销售高手为大! 广东徐闻县是全国菠萝之乡,到处都是一眼望不到边的...
    沧海一妮阅读 326评论 0 2
  • 关注“裤衩”很久,在新浪微博极少点开的今天,他是我少有的几个每开必去的微博账号,不为别的,就为言语的肆无忌惮和各种...
    窦小米阅读 840评论 1 4
  • 很高兴,今天被骗了1130元,而不是更多。 今年大三专科生,刚出来找工作的时候,我有碰到很多的培训机构,都是先交钱...
    带着面具的陌生人阅读 322评论 4 2