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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市间唉,隨后出現(xiàn)的幾起案子绞灼,更是在濱河造成了極大的恐慌,老刑警劉巖呈野,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件低矮,死亡現(xiàn)場離奇詭異,居然都是意外死亡被冒,警方通過查閱死者的電腦和手機(jī)军掂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門轮蜕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蝗锥,你說我怎么就攤上這事跃洛。” “怎么了终议?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵汇竭,是天一觀的道長。 經(jīng)常有香客問我穴张,道長细燎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任皂甘,我火速辦了婚禮玻驻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘偿枕。我一直安慰自己璧瞬,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布益老。 她就那樣靜靜地躺著彪蓬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪捺萌。 梳的紋絲不亂的頭發(fā)上档冬,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機(jī)與錄音桃纯,去河邊找鬼酷誓。 笑死,一個胖子當(dāng)著我的面吹牛态坦,可吹牛的內(nèi)容都是我干的盐数。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼伞梯,長吁一口氣:“原來是場噩夢啊……” “哼玫氢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谜诫,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤漾峡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后喻旷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體生逸,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了槽袄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烙无。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖遍尺,靈堂內(nèi)的尸體忽然破棺而出截酷,到底是詐尸還是另有隱情,我是刑警寧澤狮鸭,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布合搅,位于F島的核電站,受9級特大地震影響歧蕉,放射性物質(zhì)發(fā)生泄漏灾部。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一惯退、第九天 我趴在偏房一處隱蔽的房頂上張望赌髓。 院中可真熱鬧,春花似錦催跪、人聲如沸锁蠕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荣倾。三九已至,卻和暖如春骑丸,著一層夾襖步出監(jiān)牢的瞬間舌仍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工通危, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留铸豁,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓菊碟,卻偏偏與公主長得像节芥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子逆害,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容

  • “其實你我都一樣头镊,終將被遺忘,郭源潮魄幕。 你的病也和我的一樣相艇,風(fēng)月難扯,離合不騷梅垄。 層樓終究誤少...
    南云淵閱讀 203評論 0 4
  • 1厂捞、我想以后從事農(nóng)業(yè),現(xiàn)在應(yīng)該怎么準(zhǔn)備 答:先成為銷售高手為大队丝! 廣東徐聞縣是全國菠蘿之鄉(xiāng)靡馁,到處都是一眼望不到邊的...
    滄海一妮閱讀 333評論 0 2
  • 關(guān)注“褲衩”很久,在新浪微博極少點開的今天侠畔,他是我少有的幾個每開必去的微博賬號结缚,不為別的,就為言語的肆無忌憚和各種...
    竇小米閱讀 862評論 1 4
  • 很高興软棺,今天被騙了1130元红竭,而不是更多。 今年大三尨洌科生茵宪,剛出來找工作的時候,我有碰到很多的培訓(xùn)機(jī)構(gòu)瘦棋,都是先交錢...
    帶著面具的陌生人閱讀 332評論 4 2