OSDN Git Service

Adding ContactManager sample to SDK samples.
authorTrevor Johns <tjohns@google.com>
Tue, 24 Nov 2009 13:16:35 +0000 (05:16 -0800)
committerTrevor Johns <tjohns@google.com>
Wed, 9 Dec 2009 00:51:39 +0000 (16:51 -0800)
This sample demonstrates use of the ContactsContract classes introduced in API Level 5.

13 files changed:
samples/ContactManager/Android.mk [new file with mode: 0644]
samples/ContactManager/AndroidManifest.xml [new file with mode: 0644]
samples/ContactManager/_index.html [new file with mode: 0644]
samples/ContactManager/res/drawable-hdpi/icon.png [new file with mode: 0644]
samples/ContactManager/res/drawable-ldpi/icon.png [new file with mode: 0644]
samples/ContactManager/res/drawable-mdpi/icon.png [new file with mode: 0644]
samples/ContactManager/res/layout/account_entry.xml [new file with mode: 0644]
samples/ContactManager/res/layout/contact_adder.xml [new file with mode: 0644]
samples/ContactManager/res/layout/contact_entry.xml [new file with mode: 0644]
samples/ContactManager/res/layout/contact_manager.xml [new file with mode: 0644]
samples/ContactManager/res/values/strings.xml [new file with mode: 0644]
samples/ContactManager/src/com/google/example/android/contactmanager/ContactAdder.java [new file with mode: 0644]
samples/ContactManager/src/com/google/example/android/contactmanager/ContactManager.java [new file with mode: 0644]

diff --git a/samples/ContactManager/Android.mk b/samples/ContactManager/Android.mk
new file mode 100644 (file)
index 0000000..c585716
--- /dev/null
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := ContactManager
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/ContactManager/AndroidManifest.xml b/samples/ContactManager/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..c0a01cd
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.example.android.contactmanager"
+    android:versionCode="1" android:versionName="1.0">
+    <application android:label="@string/app_name" android:icon="@drawable/icon">
+        <activity android:name=".ContactManager" android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name="ContactAdder" android:label="@string/addContactTitle">
+        </activity>
+
+    </application>
+    <uses-sdk android:minSdkVersion="5" android:targetSdkVersion="5" />
+    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+</manifest>
diff --git a/samples/ContactManager/_index.html b/samples/ContactManager/_index.html
new file mode 100644 (file)
index 0000000..1fd51f0
--- /dev/null
@@ -0,0 +1,10 @@
+<p>A sample application that demonstrates how to manually query the system contacts provider using
+the new <code><a
+href="../../../reference/android/provider/ContactsContract.html">ContactsContract</a></code> API, as
+well as manually insert contacts into a specific account.</p>
+
+<p>In most cases, you will want to ask the existing Contacts activity to handle these tasks for you,
+resulting in a more consistent user experience.</p>
+
+<img alt="Screenshot 1" src="../images/ContactManager1.png" />
+<img alt="Screenshot 2" src="../images/ContactManager2.png" />
\ No newline at end of file
diff --git a/samples/ContactManager/res/drawable-hdpi/icon.png b/samples/ContactManager/res/drawable-hdpi/icon.png
new file mode 100644 (file)
index 0000000..8074c4c
Binary files /dev/null and b/samples/ContactManager/res/drawable-hdpi/icon.png differ
diff --git a/samples/ContactManager/res/drawable-ldpi/icon.png b/samples/ContactManager/res/drawable-ldpi/icon.png
new file mode 100644 (file)
index 0000000..1095584
Binary files /dev/null and b/samples/ContactManager/res/drawable-ldpi/icon.png differ
diff --git a/samples/ContactManager/res/drawable-mdpi/icon.png b/samples/ContactManager/res/drawable-mdpi/icon.png
new file mode 100644 (file)
index 0000000..a07c69f
Binary files /dev/null and b/samples/ContactManager/res/drawable-mdpi/icon.png differ
diff --git a/samples/ContactManager/res/layout/account_entry.xml b/samples/ContactManager/res/layout/account_entry.xml
new file mode 100644 (file)
index 0000000..435e737
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="?android:attr/listPreferredItemHeight"
+    android:padding="6dip">
+    <ImageView
+        android:id="@+id/accountIcon"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentBottom="true"
+        android:layout_marginRight="6dip" />
+    <TextView
+        android:id="@+id/secondAccountLine"
+        android:layout_width="fill_parent"
+        android:layout_height="26dip"
+        android:layout_toRightOf="@id/accountIcon"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentRight="true"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:textColor="@android:color/secondary_text_light" />
+    <TextView
+        android:id="@+id/firstAccountLine"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/accountIcon"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentTop="true"
+        android:layout_above="@id/secondAccountLine"
+        android:layout_alignWithParentIfMissing="true"
+        android:gravity="center_vertical"
+        android:textColor="@android:color/primary_text_light"/>
+</RelativeLayout>
diff --git a/samples/ContactManager/res/layout/contact_adder.xml b/samples/ContactManager/res/layout/contact_adder.xml
new file mode 100644 (file)
index 0000000..92d06f3
--- /dev/null
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent">
+    <TableLayout android:layout_width="fill_parent"
+                 android:layout_height="fill_parent">
+        <TableRow>
+            <TextView android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:text="@string/targetAccountLabel"/>
+        </TableRow>
+        <TableRow>
+            <Spinner android:layout_height="wrap_content"
+                     android:layout_width="fill_parent"
+                     android:layout_weight="1"
+                     android:id="@+id/accountSpinner"/>
+        </TableRow>
+        <TableRow>
+            <TextView android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:text="@string/contactNameLabel"/>
+        </TableRow>
+        <TableRow>
+            <EditText android:id="@+id/contactNameEditText"
+                      android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:layout_weight="1"/>
+        </TableRow>
+        <TableRow>
+            <TextView android:text="@string/contactPhoneLabel"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"/>
+        </TableRow>
+        <TableRow>
+            <EditText android:id="@+id/contactPhoneEditText"
+                      android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:layout_weight="1"/>
+            <Spinner android:id="@+id/contactPhoneTypeSpinner"
+                     android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"/>
+        </TableRow>
+        <TableRow>
+            <TextView android:text="@string/contactEmailLabel"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"/>
+        </TableRow>
+        <TableRow>
+            <EditText android:id="@+id/contactEmailEditText"
+                      android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:layout_weight="1"/>
+            <Spinner android:id="@+id/contactEmailTypeSpinner"
+                     android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"/>
+        </TableRow>
+        <TableRow>
+            <Button android:layout_height="wrap_content"
+                    android:text="@string/save"
+                    android:id="@+id/contactSaveButton"
+                    android:layout_width="fill_parent"
+                    android:layout_weight="1"/>
+        </TableRow>
+    </TableLayout>
+</ScrollView>
diff --git a/samples/ContactManager/res/layout/contact_entry.xml b/samples/ContactManager/res/layout/contact_entry.xml
new file mode 100644 (file)
index 0000000..9909025
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content">
+    <TextView android:text="@+id/contactEntryText"
+              android:id="@+id/contactEntryText"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/samples/ContactManager/res/layout/contact_manager.xml b/samples/ContactManager/res/layout/contact_manager.xml
new file mode 100644 (file)
index 0000000..73f6f81
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent">
+    <ListView android:layout_width="fill_parent"
+              android:id="@+id/contactList"
+              android:layout_height="wrap_content"
+              android:layout_weight="1"/>
+    <CheckBox android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:id="@+id/showInvisible"
+              android:text="@string/showInvisible"/>
+    <Button android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:id="@+id/addContactButton"
+            android:text="@string/addContactButtonLabel"/>
+</LinearLayout>
diff --git a/samples/ContactManager/res/values/strings.xml b/samples/ContactManager/res/values/strings.xml
new file mode 100644 (file)
index 0000000..c7a65b4
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="accountSpinnerLabel">Account</string>
+    <string name="addContactButtonLabel">Add Contact</string>
+    <string name="addContactTitle">Add Contact</string>
+    <string name="allAccounts">All Accounts</string>
+    <string name="app_name">Contact Manager</string>
+    <string name="contactCreationFailure">Contact creation failed, check logs.</string>
+    <string name="contactEmailLabel">Contact Email</string>
+    <string name="contactNameLabel">Contact Name</string>
+    <string name="contactPhoneLabel">Contact Phone</string>
+    <string name="save">Save</string>
+    <string name="selectAccountLabel">Select</string>
+    <string name="selectLabel">Select label</string>
+    <string name="showInvisible">Show Invisible Contacts (Only)</string>
+    <string name="targetAccountLabel">Target Account</string>
+    <string name="undefinedTypeLabel">(Undefined)</string>
+</resources>
diff --git a/samples/ContactManager/src/com/google/example/android/contactmanager/ContactAdder.java b/samples/ContactManager/src/com/google/example/android/contactmanager/ContactAdder.java
new file mode 100644 (file)
index 0000000..92ad5a2
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.example.android.contactmanager;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
+import android.accounts.OnAccountsUpdateListener;
+import android.app.Activity;
+import android.content.ContentProviderOperation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+public final class ContactAdder extends Activity implements OnAccountsUpdateListener
+{
+    public static final String TAG = "ContactsAdder";
+    public static final String ACCOUNT_NAME =
+            "com.google.example.android.contactmanager.ContactsAdder.ACCOUNT_NAME";
+    public static final String ACCOUNT_TYPE =
+            "com.google.example.android.contactmanager.ContactsAdder.ACCOUNT_TYPE";
+
+    private ArrayList<AccountData> mAccounts;
+    private AccountAdapter mAccountAdapter;
+    private Spinner mAccountSpinner;
+    private EditText mContactEmailEditText;
+    private ArrayList<Integer> mContactEmailTypes;
+    private Spinner mContactEmailTypeSpinner;
+    private EditText mContactNameEditText;
+    private EditText mContactPhoneEditText;
+    private ArrayList<Integer> mContactPhoneTypes;
+    private Spinner mContactPhoneTypeSpinner;
+    private Button mContactSaveButton;
+    private AccountData mSelectedAccount;
+
+    /**
+     * Called when the activity is first created. Responsible for initializing the UI.
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        Log.v(TAG, "Activity State: onCreate()");
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.contact_adder);
+
+        // Obtain handles to UI objects
+        mAccountSpinner = (Spinner) findViewById(R.id.accountSpinner);
+        mContactNameEditText = (EditText) findViewById(R.id.contactNameEditText);
+        mContactPhoneEditText = (EditText) findViewById(R.id.contactPhoneEditText);
+        mContactEmailEditText = (EditText) findViewById(R.id.contactEmailEditText);
+        mContactPhoneTypeSpinner = (Spinner) findViewById(R.id.contactPhoneTypeSpinner);
+        mContactEmailTypeSpinner = (Spinner) findViewById(R.id.contactEmailTypeSpinner);
+        mContactSaveButton = (Button) findViewById(R.id.contactSaveButton);
+
+        // Prepare list of supported account types
+        // Note: Other types are available in ContactsContract.CommonDataKinds
+        //       Also, be aware that type IDs differ between Phone and Email, and MUST be computed
+        //       separately.
+        mContactPhoneTypes = new ArrayList<Integer>();
+        mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_HOME);
+        mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_WORK);
+        mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
+        mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_OTHER);
+        mContactEmailTypes = new ArrayList<Integer>();
+        mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_HOME);
+        mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_WORK);
+        mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_MOBILE);
+        mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_OTHER);
+
+        // Prepare model for account spinner
+        mAccounts = new ArrayList<AccountData>();
+        mAccountAdapter = new AccountAdapter(this, mAccounts);
+        mAccountSpinner.setAdapter(mAccountAdapter);
+
+        // Populate list of account types for phone
+        ArrayAdapter<String> adapter;
+        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        Iterator<Integer> iter;
+        iter = mContactPhoneTypes.iterator();
+        while (iter.hasNext()) {
+            adapter.add(ContactsContract.CommonDataKinds.Phone.getTypeLabel(
+                    this.getResources(),
+                    iter.next(),
+                    getString(R.string.undefinedTypeLabel)).toString());
+        }
+        mContactPhoneTypeSpinner.setAdapter(adapter);
+        mContactPhoneTypeSpinner.setPrompt(getString(R.string.selectLabel));
+
+        // Populate list of account types for email
+        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        iter = mContactEmailTypes.iterator();
+        while (iter.hasNext()) {
+            adapter.add(ContactsContract.CommonDataKinds.Email.getTypeLabel(
+                    this.getResources(),
+                    iter.next(),
+                    getString(R.string.undefinedTypeLabel)).toString());
+        }
+        mContactEmailTypeSpinner.setAdapter(adapter);
+        mContactEmailTypeSpinner.setPrompt(getString(R.string.selectLabel));
+
+        // Prepare the system account manager. On registering the listener below, we also ask for
+        // an initial callback to pre-populate the account list.
+        AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
+
+        // Register handlers for UI elements
+        mAccountSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long i) {
+                updateAccountSelection();
+            }
+
+            public void onNothingSelected(AdapterView<?> parent) {
+                // We don't need to worry about nothing being selected, since Spinners don't allow
+                // this.
+            }
+        });
+        mContactSaveButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                onSaveButtonClicked();
+            }
+        });
+    }
+
+    /**
+     * Actions for when the Save button is clicked. Creates a contact entry and terminates the
+     * activity.
+     */
+    private void onSaveButtonClicked() {
+        Log.v(TAG, "Save button clicked");
+        createContactEntry();
+        finish();
+    }
+
+    /**
+     * Creates a contact entry from the current UI values in the account named by mSelectedAccount.
+     */
+    protected void createContactEntry() {
+        // Get values from UI
+        String name = mContactNameEditText.getText().toString();
+        String phone = mContactPhoneEditText.getText().toString();
+        String email = mContactEmailEditText.getText().toString();
+        int phoneType = mContactPhoneTypes.get(
+                mContactPhoneTypeSpinner.getSelectedItemPosition());
+        int emailType = mContactEmailTypes.get(
+                mContactEmailTypeSpinner.getSelectedItemPosition());;
+
+        // Prepare contact creation request
+        //
+        // Note: We use RawContacts because this data must be associated with a particular account.
+        //       The system will aggregate this with any other data for this contact and create a
+        //       coresponding entry in the ContactsContract.Contacts provider for us.
+        ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+        ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
+                .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
+                .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName())
+                .build());
+        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+                .withValue(ContactsContract.Data.MIMETYPE,
+                        ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+                .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
+                .build());
+        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+                .withValue(ContactsContract.Data.MIMETYPE,
+                        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+                .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
+                .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType)
+                .build());
+        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+                .withValue(ContactsContract.Data.MIMETYPE,
+                        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
+                .withValue(ContactsContract.CommonDataKinds.Email.DATA, email)
+                .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType)
+                .build());
+
+        // Ask the Contact provider to create a new contact
+        Log.i(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +
+                mSelectedAccount.getType() + ")");
+        Log.i(TAG,"Creating contact: " + name);
+        try {
+            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
+        } catch (Exception e) {
+            // Display 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, "Exceptoin encoutered while inserting contact: " + e);
+        }
+    }
+
+    /**
+     * Called when this activity is about to be destroyed by the system.
+     */
+    @Override
+    public void onDestroy() {
+        // Remove AccountManager callback
+        AccountManager.get(this).removeOnAccountsUpdatedListener(this);
+        super.onDestroy();
+    }
+
+    /**
+     * Updates account list spinner when the list of Accounts on the system changes. Satisfies
+     * OnAccountsUpdateListener implementation.
+     */
+    public void onAccountsUpdated(Account[] a) {
+        Log.i(TAG, "Account list update detected");
+        // Clear out any old data to prevent duplicates
+        mAccounts.clear();
+
+        // Get account data from system
+        AuthenticatorDescription[] accountTypes = AccountManager.get(this).getAuthenticatorTypes();
+
+        // Populate tables
+        for (int i = 0; i < a.length; i++) {
+            // The user may have multiple accounts with the same name, so we need to construct a
+            // meaningful display name for each.
+            String systemAccountType = a[i].type;
+            AuthenticatorDescription ad = getAuthenticatorDescription(systemAccountType,
+                    accountTypes);
+            AccountData data = new AccountData(a[i].name, ad);
+            mAccounts.add(data);
+        }
+
+        // Update the account spinner
+        mAccountAdapter.notifyDataSetChanged();
+    }
+
+    /**
+     * Obtain the AuthenticatorDescription for a given account type.
+     * @param type The account type to locate.
+     * @param dictionary An array of AuthenticatorDescriptions, as returned by AccountManager.
+     * @return The description for the specified account type.
+     */
+    private static AuthenticatorDescription getAuthenticatorDescription(String type,
+            AuthenticatorDescription[] dictionary) {
+        for (int i = 0; i < dictionary.length; i++) {
+            if (dictionary[i].type.equals(type)) {
+                return dictionary[i];
+            }
+        }
+        // No match found
+        throw new RuntimeException("Unable to find matching authenticator");
+    }
+
+    /**
+     * Update account selection. If NO_ACCOUNT is selected, then we prohibit inserting new contacts.
+     */
+    private void updateAccountSelection() {
+        // Read current account selection
+        mSelectedAccount = (AccountData) mAccountSpinner.getSelectedItem();
+    }
+
+    /**
+     * A container class used to repreresent all known information about an account.
+     */
+    private class AccountData {
+        private String mName;
+        private String mType;
+        private CharSequence mTypeLabel;
+        private Drawable mIcon;
+
+        /**
+         * @param name The name of the account. This is usually the user's email address or
+         *        username.
+         * @param description The description for this account. This will be dictated by the
+         *        type of account returned, and can be obtained from the system AccountManager.
+         */
+        public AccountData(String name, AuthenticatorDescription description) {
+            mName = name;
+            if (description != null) {
+                mType = description.type;
+
+                // The type string is stored in a resource, so we need to convert it into something
+                // human readable.
+                String packageName = description.packageName;
+                PackageManager pm = getPackageManager();
+
+                if (description.labelId != 0) {
+                    mTypeLabel = pm.getText(packageName, description.labelId, null);
+                    if (mTypeLabel == null) {
+                        throw new IllegalArgumentException("LabelID provided, but label not found");
+                    }
+                } else {
+                    mTypeLabel = "";
+                }
+
+                if (description.iconId != 0) {
+                    mIcon = pm.getDrawable(packageName, description.iconId, null);
+                    if (mIcon == null) {
+                        throw new IllegalArgumentException("IconID provided, but drawable not " +
+                                "found");
+                    }
+                } else {
+                    mIcon = getResources().getDrawable(android.R.drawable.sym_def_app_icon);
+                }
+            }
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public String getType() {
+            return mType;
+        }
+
+        public CharSequence getTypeLabel() {
+            return mTypeLabel;
+        }
+
+        public Drawable getIcon() {
+            return mIcon;
+        }
+
+        public String toString() {
+            return mName;
+        }
+    }
+
+    /**
+     * Custom adapter used to display account icons and descriptions in the account spinner.
+     */
+    private class AccountAdapter extends ArrayAdapter<AccountData> {
+        public AccountAdapter(Context context, ArrayList<AccountData> accountData) {
+            super(context, android.R.layout.simple_spinner_item, accountData);
+            setDropDownViewResource(R.layout.account_entry);
+        }
+
+        public View getDropDownView(int position, View convertView, ViewGroup parent) {
+            // Inflate a view template
+            if (convertView == null) {
+                LayoutInflater layoutInflater = getLayoutInflater();
+                convertView = layoutInflater.inflate(R.layout.account_entry, parent, false);
+            }
+            TextView firstAccountLine = (TextView) convertView.findViewById(R.id.firstAccountLine);
+            TextView secondAccountLine = (TextView) convertView.findViewById(R.id.secondAccountLine);
+            ImageView accountIcon = (ImageView) convertView.findViewById(R.id.accountIcon);
+
+            // Populate template
+            AccountData data = getItem(position);
+            firstAccountLine.setText(data.getName());
+            secondAccountLine.setText(data.getTypeLabel());
+            Drawable icon = data.getIcon();
+            if (icon == null) {
+                icon = getResources().getDrawable(android.R.drawable.ic_menu_search);
+            }
+            accountIcon.setImageDrawable(icon);
+            return convertView;
+        }
+    }
+}
diff --git a/samples/ContactManager/src/com/google/example/android/contactmanager/ContactManager.java b/samples/ContactManager/src/com/google/example/android/contactmanager/ContactManager.java
new file mode 100644 (file)
index 0000000..c4d9803
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.example.android.contactmanager;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+
+public final class ContactManager extends Activity
+{
+
+    public static final String TAG = "ContactManager";
+
+    private Button mAddAccountButton;
+    private ListView mContactList;
+    private boolean mShowInvisible;
+    private CheckBox mShowInvisibleControl;
+
+    /**
+     * Called when the activity is first created. Responsible for initializing the UI.
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        Log.v(TAG, "Activity State: onCreate()");
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.contact_manager);
+
+        // Obtain handles to UI objects
+        mAddAccountButton = (Button) findViewById(R.id.addContactButton);
+        mContactList = (ListView) findViewById(R.id.contactList);
+        mShowInvisibleControl = (CheckBox) findViewById(R.id.showInvisible);
+
+        // Initialize class properties
+        mShowInvisible = false;
+        mShowInvisibleControl.setChecked(mShowInvisible);
+
+        // Register handler for UI elements
+        mAddAccountButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                Log.d(TAG, "mAddAccountButton clicked");
+                launchContactAdder();
+            }
+        });
+        mShowInvisibleControl.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                Log.d(TAG, "mShowInvisibleControl changed: " + isChecked);
+                mShowInvisible = isChecked;
+                populateContactList();
+            }
+        });
+
+        // Populate the contact list
+        populateContactList();
+    }
+
+    /**
+     * Populate the contact list based on account currently selected in the account spinner.
+     */
+    private void populateContactList() {
+        // Build adapter with contact entries
+        Cursor cursor = getContacts();
+        String[] fields = new String[] {
+                ContactsContract.Data.DISPLAY_NAME
+        };
+        SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.contact_entry, cursor,
+                fields, new int[] {R.id.contactEntryText});
+        mContactList.setAdapter(adapter);
+    }
+
+    /**
+     * Obtains the contact list for the currently selected account.
+     *
+     * @return A cursor for for accessing the contact list.
+     */
+    private Cursor getContacts()
+    {
+        // Run query
+        Uri uri = ContactsContract.Contacts.CONTENT_URI;
+        String[] projection = new String[] {
+                ContactsContract.Contacts.LOOKUP_KEY,
+                ContactsContract.Contacts.DISPLAY_NAME
+        };
+        String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" +
+                (mShowInvisible ? "0" : "1") + "'";
+        String[] selectionArgs = null;
+        String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
+
+        return managedQuery(uri, projection, selection, selectionArgs, sortOrder);
+    }
+
+    /**
+     * Launches the ContactAdder activity to add a new contact to the selected accont.
+     */
+    protected void launchContactAdder() {
+        Intent i = new Intent(this, ContactAdder.class);
+        startActivity(i);
+    }
+}