Initial Contribution
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..777b053
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := user development
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := Contacts
+LOCAL_CERTIFICATE := shared
+
+include $(BUILD_PACKAGE)
+
+# Use the folloing include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..8445355
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.android.contacts"
+        android:sharedUserId="android.uid.shared"
+>
+    <uses-permission android:name="android.permission.CALL_PRIVILEGED" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+
+    <application
+        android:label="@string/contactsList"
+        android:icon="@drawable/ic_launcher_contacts"
+        android:process="android.process.acore"
+        android:taskAffinity="android.task.contacts"
+    >
+
+        <!-- A virtual 12 key dialer -->
+        <activity android:name="TwelveKeyDialer"
+            android:launchMode="singleTop"
+        >
+            <intent-filter>
+                <action android:name="com.android.phone.action.TOUCH_DIALER" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.TAB" />
+            </intent-filter>
+        </activity>
+
+        <!-- A list of recent calls -->
+        <activity android:name="RecentCallsListActivity"
+            android:label="@string/recentCallsIconLabel"
+        >
+            <intent-filter>
+                <action android:name="com.android.phone.action.RECENT_CALLS" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.TAB" />
+            </intent-filter>
+        </activity>
+
+        <!-- Tab container for TwelveKeyDialer and RecentCallsList -->
+        <activity android:name="DialtactsActivity"
+            android:label="@string/dialerIconLabel"
+            android:theme="@android:style/Theme.NoTitleBar"
+            android:launchMode="singleTask"
+            android:clearTaskOnLaunch="true"
+            android:icon="@drawable/ic_launcher_phone"
+        >
+            <intent-filter>
+                <action android:name="android.intent.action.DIAL" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:mimeType="vnd.android.cursor.item/phone" />
+                <data android:mimeType="vnd.android.cursor.item/person" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.DIAL" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="voicemail" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.DIAL" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+                <category android:name="android.intent.category.BROWSABLE" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <action android:name="android.intent.action.DIAL" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="tel" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:mimeType="vnd.android.cursor.dir/calls" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.CALL_BUTTON" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+            </intent-filter>
+        </activity>
+        
+        <!-- An empty activity that presents the DialtactActivity's Contacts tab -->
+        <activity-alias android:name="DialtactsContactsEntryActivity"
+            android:targetActivity="DialtactsActivity"
+            android:label="@string/contactsIconLabel"
+            android:icon="@drawable/ic_launcher_contacts"
+        >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.dir/person" />
+            </intent-filter>
+            
+            <intent-filter>
+                <action android:name="com.android.contacts.action.FILTER_CONTACTS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity-alias>
+
+        <!-- The actual list of contacts, usually embedded in ContactsActivity -->
+        <activity android:name="ContactsListActivity"
+            android:label="@string/contactsList"
+            android:clearTaskOnLaunch="true"
+        >
+            <intent-filter>
+                <action android:name="com.android.contacts.action.LIST_DEFAULT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.TAB" />
+            </intent-filter>
+
+            <intent-filter>
+                <action android:name="com.android.contacts.action.LIST_CONTACTS" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.TAB" />
+            </intent-filter>
+
+            <intent-filter>
+                <action android:name="com.android.contacts.action.LIST_ALL_CONTACTS" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.TAB" />
+            </intent-filter>
+
+            <intent-filter>
+                <action android:name="com.android.contacts.action.LIST_CONTACTS_WITH_PHONES" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.TAB" />
+            </intent-filter>
+
+            <intent-filter android:label="@string/starredList">
+                <action android:name="com.android.contacts.action.LIST_STARRED" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.TAB" />
+            </intent-filter>
+            
+            <intent-filter android:label="@string/frequentList">
+                <action android:name="com.android.contacts.action.LIST_FREQUENT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.TAB" />
+            </intent-filter>
+
+            <intent-filter android:label="@string/strequentList">
+                <action android:name="com.android.contacts.action.LIST_STREQUENT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.TAB" />
+            </intent-filter>
+
+            <intent-filter>
+                <action android:name="android.intent.action.INSERT_OR_EDIT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.item/person" />
+            </intent-filter>
+<!--
+            <intent-filter android:label="Add To Contacts">
+                <action android:name="com.android.contacts.action.ADD_CONTACT" />
+                <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
+                <data android:scheme="mailto" />
+                <data android:scheme="tel" />
+            </intent-filter>
+-->
+            <intent-filter>
+                <action android:name="android.intent.action.PICK" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.dir/person" />
+                <data android:mimeType="vnd.android.cursor.dir/phone" />
+                <data android:mimeType="vnd.android.cursor.dir/postal-address" />
+            </intent-filter>
+
+            <intent-filter>
+                <action android:name="android.intent.action.GET_CONTENT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.item/person" />
+                <data android:mimeType="vnd.android.cursor.item/phone" />
+                <data android:mimeType="vnd.android.cursor.item/postal-address" />
+            </intent-filter>
+
+            <intent-filter>
+                <action android:name="android.intent.action.SEARCH" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+
+            <meta-data android:name="android.app.searchable"
+                android:resource="@xml/searchable"
+            />
+        </activity>
+
+        <activity-alias android:name="ContactShortcut"
+            android:targetActivity="ContactsListActivity"
+            android:label="@string/shortcutContact">
+
+            <intent-filter>
+                <action android:name="android.intent.action.CREATE_SHORTCUT" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+
+        </activity-alias>
+
+        <!-- Views the details of a single contact -->
+        <activity android:name="ViewContactActivity"
+            android:label="@string/viewContactTitle"
+            android:theme="@android:style/Theme.NoTitleBar"
+        >
+            <intent-filter android:label="@string/viewContactDesription">
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.item/person" />
+            </intent-filter>
+        </activity>
+
+        <!-- Edits the details of a single contact -->
+        <activity android:name="EditContactActivity">
+            <intent-filter android:label="@string/editContactDescription">
+                <action android:name="android.intent.action.EDIT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.item/person" />
+            </intent-filter>
+            <intent-filter android:label="@string/insertContactDescription">
+                <action android:name="android.intent.action.INSERT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.dir/person" />
+            </intent-filter>
+        </activity>
+
+        <!-- Attaches a photo to a contact. Started from external applications -->
+        <activity android:name="AttachImage"
+            android:label="@string/attachToContact"
+            android:taskAffinity="">
+            <intent-filter>
+                <action android:name="android.intent.action.ATTACH_DATA" />
+                <data android:mimeType="image/*" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            />
+        </activity>        
+
+        <!-- Preferences activity -->
+        <activity android:name="ContactsPreferenceActivity" />
+
+        <!-- Activity used to select the groups that should be synced -->
+        <activity android:name="ContactsGroupSyncSelector" />
+
+        <!-- Makes .ContactsListActivity the search target for any activity in Contacts -->
+        <meta-data android:name="android.app.default_searchable" 
+                   android:value=".ContactsListActivity" />
+        
+    </application>
+</manifest>
+
+
+
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/res/color-finger/dialer_button_text.xml b/res/color-finger/dialer_button_text.xml
new file mode 100644
index 0000000..9e71988
--- /dev/null
+++ b/res/color-finger/dialer_button_text.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true" android:color="#000"/>
+    <item android:state_focused="true" android:color="#000"/>
+    <item android:color="#FFF"/> <!-- not selected -->
+
+</selector>
+
diff --git a/res/drawable-finger/backpanel_bottom_white.9.png b/res/drawable-finger/backpanel_bottom_white.9.png
new file mode 100644
index 0000000..ffcc228
--- /dev/null
+++ b/res/drawable-finger/backpanel_bottom_white.9.png
Binary files differ
diff --git a/res/drawable-finger/border.xml b/res/drawable-finger/border.xml
new file mode 100644
index 0000000..b5d2f28
--- /dev/null
+++ b/res/drawable-finger/border.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/border_selected" />
+    <item android:state_pressed="true" android:drawable="@drawable/border_pressed" />
+    <item android:drawable="@drawable/border_default" />
+</selector>
+
diff --git a/res/drawable-finger/border_default.9.png b/res/drawable-finger/border_default.9.png
new file mode 100644
index 0000000..e65051d
--- /dev/null
+++ b/res/drawable-finger/border_default.9.png
Binary files differ
diff --git a/res/drawable-finger/border_pressed.9.png b/res/drawable-finger/border_pressed.9.png
new file mode 100644
index 0000000..74bc281
--- /dev/null
+++ b/res/drawable-finger/border_pressed.9.png
Binary files differ
diff --git a/res/drawable-finger/border_selected.9.png b/res/drawable-finger/border_selected.9.png
new file mode 100644
index 0000000..e1d6cc1
--- /dev/null
+++ b/res/drawable-finger/border_selected.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial.xml b/res/drawable-finger/btn_dial.xml
new file mode 100644
index 0000000..4dfcd64
--- /dev/null
+++ b/res/drawable-finger/btn_dial.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true"
+        android:drawable="@drawable/btn_dial_pressed" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_dial_selected" />
+    <item
+        android:drawable="@drawable/btn_dial_normal" />
+</selector>
diff --git a/res/drawable-finger/btn_dial_delete.xml b/res/drawable-finger/btn_dial_delete.xml
new file mode 100644
index 0000000..235554d
--- /dev/null
+++ b/res/drawable-finger/btn_dial_delete.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/btn_dial_delete_pressed" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_dial_delete_selected" />
+    <item
+        android:drawable="@drawable/btn_dial_delete_normal" />
+</selector>
+
diff --git a/res/drawable-finger/btn_dial_delete_activated.9.png b/res/drawable-finger/btn_dial_delete_activated.9.png
new file mode 100644
index 0000000..453a368
--- /dev/null
+++ b/res/drawable-finger/btn_dial_delete_activated.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_delete_active.xml b/res/drawable-finger/btn_dial_delete_active.xml
new file mode 100644
index 0000000..3403f34
--- /dev/null
+++ b/res/drawable-finger/btn_dial_delete_active.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/btn_dial_delete_pressed" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_dial_delete_selected" />
+    <item
+        android:drawable="@drawable/btn_dial_delete_activated" />
+</selector>
+
diff --git a/res/drawable-finger/btn_dial_delete_normal.9.png b/res/drawable-finger/btn_dial_delete_normal.9.png
new file mode 100644
index 0000000..9da1cff
--- /dev/null
+++ b/res/drawable-finger/btn_dial_delete_normal.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_delete_pressed.9.png b/res/drawable-finger/btn_dial_delete_pressed.9.png
new file mode 100644
index 0000000..5bbc20d
--- /dev/null
+++ b/res/drawable-finger/btn_dial_delete_pressed.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_delete_selected.9.png b/res/drawable-finger/btn_dial_delete_selected.9.png
new file mode 100644
index 0000000..d959768
--- /dev/null
+++ b/res/drawable-finger/btn_dial_delete_selected.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_normal.png b/res/drawable-finger/btn_dial_normal.png
new file mode 100644
index 0000000..ff8796b
--- /dev/null
+++ b/res/drawable-finger/btn_dial_normal.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_pressed.png b/res/drawable-finger/btn_dial_pressed.png
new file mode 100644
index 0000000..3c198c4
--- /dev/null
+++ b/res/drawable-finger/btn_dial_pressed.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_selected.png b/res/drawable-finger/btn_dial_selected.png
new file mode 100644
index 0000000..8ca2b0d
--- /dev/null
+++ b/res/drawable-finger/btn_dial_selected.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_textfield.xml b/res/drawable-finger/btn_dial_textfield.xml
new file mode 100644
index 0000000..4eabf18
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/btn_dial_textfield_pressed" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_dial_textfield_selected" />
+    <item
+        android:drawable="@drawable/btn_dial_textfield_normal" />
+</selector>
+
diff --git a/res/drawable-finger/btn_dial_textfield_activated.9.png b/res/drawable-finger/btn_dial_textfield_activated.9.png
new file mode 100644
index 0000000..4c34576
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_activated.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_textfield_activated_full.9.png b/res/drawable-finger/btn_dial_textfield_activated_full.9.png
new file mode 100644
index 0000000..f0a571c
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_activated_full.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_textfield_active.xml b/res/drawable-finger/btn_dial_textfield_active.xml
new file mode 100644
index 0000000..18b84c6
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_active.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/btn_dial_textfield_pressed" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_dial_textfield_selected" />
+    <item
+        android:drawable="@drawable/btn_dial_textfield_activated" />
+</selector>
+
diff --git a/res/drawable-finger/btn_dial_textfield_normal.9.png b/res/drawable-finger/btn_dial_textfield_normal.9.png
new file mode 100644
index 0000000..40ab866
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_normal.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_textfield_normal_full.9.png b/res/drawable-finger/btn_dial_textfield_normal_full.9.png
new file mode 100644
index 0000000..261d130
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_normal_full.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_textfield_pressed.9.png b/res/drawable-finger/btn_dial_textfield_pressed.9.png
new file mode 100644
index 0000000..0087327
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_pressed.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_textfield_pressed_full.9.png b/res/drawable-finger/btn_dial_textfield_pressed_full.9.png
new file mode 100644
index 0000000..7db856b
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_pressed_full.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_textfield_selected.9.png b/res/drawable-finger/btn_dial_textfield_selected.9.png
new file mode 100644
index 0000000..63fceaa
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_selected.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial_textfield_selected_full.9.png b/res/drawable-finger/btn_dial_textfield_selected_full.9.png
new file mode 100644
index 0000000..2667868
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_selected_full.9.png
Binary files differ
diff --git a/res/drawable-finger/dark_header.9.png b/res/drawable-finger/dark_header.9.png
new file mode 100644
index 0000000..5b1d71f
--- /dev/null
+++ b/res/drawable-finger/dark_header.9.png
Binary files differ
diff --git a/res/drawable-finger/dark_header_dithered.xml b/res/drawable-finger/dark_header_dithered.xml
new file mode 100644
index 0000000..36994a7
--- /dev/null
+++ b/res/drawable-finger/dark_header_dithered.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/dark_header"
+    android:dither="true"
+/>
diff --git a/res/drawable-finger/dial_num_0.xml b/res/drawable-finger/dial_num_0.xml
new file mode 100644
index 0000000..cd4d727
--- /dev/null
+++ b/res/drawable-finger/dial_num_0.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_0_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_0_blk" />
+    <item
+        android:drawable="@drawable/dial_num_0_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_0_blk.png b/res/drawable-finger/dial_num_0_blk.png
new file mode 100644
index 0000000..a10c601
--- /dev/null
+++ b/res/drawable-finger/dial_num_0_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_0_wht.png b/res/drawable-finger/dial_num_0_wht.png
new file mode 100644
index 0000000..2de90a1
--- /dev/null
+++ b/res/drawable-finger/dial_num_0_wht.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_1.xml b/res/drawable-finger/dial_num_1.xml
new file mode 100644
index 0000000..48737b2
--- /dev/null
+++ b/res/drawable-finger/dial_num_1.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_1_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_1_blk" />
+    <item
+        android:drawable="@drawable/dial_num_1_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_1_blk.png b/res/drawable-finger/dial_num_1_blk.png
new file mode 100644
index 0000000..822f9ca
--- /dev/null
+++ b/res/drawable-finger/dial_num_1_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_1_wht.png b/res/drawable-finger/dial_num_1_wht.png
new file mode 100644
index 0000000..e488beb
--- /dev/null
+++ b/res/drawable-finger/dial_num_1_wht.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_2.xml b/res/drawable-finger/dial_num_2.xml
new file mode 100644
index 0000000..1a087f6
--- /dev/null
+++ b/res/drawable-finger/dial_num_2.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_2_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_2_blk" />
+    <item
+        android:drawable="@drawable/dial_num_2_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_2_blk.png b/res/drawable-finger/dial_num_2_blk.png
new file mode 100644
index 0000000..539c301
--- /dev/null
+++ b/res/drawable-finger/dial_num_2_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_2_wht.png b/res/drawable-finger/dial_num_2_wht.png
new file mode 100644
index 0000000..cd1ae8d
--- /dev/null
+++ b/res/drawable-finger/dial_num_2_wht.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_3.xml b/res/drawable-finger/dial_num_3.xml
new file mode 100644
index 0000000..dda794a
--- /dev/null
+++ b/res/drawable-finger/dial_num_3.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_3_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_3_blk" />
+    <item
+        android:drawable="@drawable/dial_num_3_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_3_blk.png b/res/drawable-finger/dial_num_3_blk.png
new file mode 100644
index 0000000..dc70f33
--- /dev/null
+++ b/res/drawable-finger/dial_num_3_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_3_wht.png b/res/drawable-finger/dial_num_3_wht.png
new file mode 100644
index 0000000..dc7c8e2
--- /dev/null
+++ b/res/drawable-finger/dial_num_3_wht.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_4.xml b/res/drawable-finger/dial_num_4.xml
new file mode 100644
index 0000000..511e58b
--- /dev/null
+++ b/res/drawable-finger/dial_num_4.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_4_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_4_blk" />
+    <item
+        android:drawable="@drawable/dial_num_4_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_4_blk.png b/res/drawable-finger/dial_num_4_blk.png
new file mode 100644
index 0000000..084db44
--- /dev/null
+++ b/res/drawable-finger/dial_num_4_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_4_wht.png b/res/drawable-finger/dial_num_4_wht.png
new file mode 100644
index 0000000..44128a1
--- /dev/null
+++ b/res/drawable-finger/dial_num_4_wht.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_5.xml b/res/drawable-finger/dial_num_5.xml
new file mode 100644
index 0000000..a857bc7
--- /dev/null
+++ b/res/drawable-finger/dial_num_5.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_5_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_5_blk" />
+    <item
+        android:drawable="@drawable/dial_num_5_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_5_blk.png b/res/drawable-finger/dial_num_5_blk.png
new file mode 100644
index 0000000..0a06f20
--- /dev/null
+++ b/res/drawable-finger/dial_num_5_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_5_wht.png b/res/drawable-finger/dial_num_5_wht.png
new file mode 100644
index 0000000..fc605b8
--- /dev/null
+++ b/res/drawable-finger/dial_num_5_wht.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_6.xml b/res/drawable-finger/dial_num_6.xml
new file mode 100644
index 0000000..b494458
--- /dev/null
+++ b/res/drawable-finger/dial_num_6.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_6_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_6_blk" />
+    <item
+        android:drawable="@drawable/dial_num_6_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_6_blk.png b/res/drawable-finger/dial_num_6_blk.png
new file mode 100644
index 0000000..2498d77
--- /dev/null
+++ b/res/drawable-finger/dial_num_6_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_6_wht.png b/res/drawable-finger/dial_num_6_wht.png
new file mode 100644
index 0000000..086e8ce
--- /dev/null
+++ b/res/drawable-finger/dial_num_6_wht.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_7.xml b/res/drawable-finger/dial_num_7.xml
new file mode 100644
index 0000000..a22c894
--- /dev/null
+++ b/res/drawable-finger/dial_num_7.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_7_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_7_blk" />
+    <item
+        android:drawable="@drawable/dial_num_7_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_7_blk.png b/res/drawable-finger/dial_num_7_blk.png
new file mode 100644
index 0000000..cfb20a7
--- /dev/null
+++ b/res/drawable-finger/dial_num_7_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_7_wht.png b/res/drawable-finger/dial_num_7_wht.png
new file mode 100644
index 0000000..cb1b097
--- /dev/null
+++ b/res/drawable-finger/dial_num_7_wht.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_8.xml b/res/drawable-finger/dial_num_8.xml
new file mode 100644
index 0000000..8ac7930
--- /dev/null
+++ b/res/drawable-finger/dial_num_8.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_8_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_8_blk" />
+    <item
+        android:drawable="@drawable/dial_num_8_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_8_blk.png b/res/drawable-finger/dial_num_8_blk.png
new file mode 100644
index 0000000..845ee68
--- /dev/null
+++ b/res/drawable-finger/dial_num_8_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_8_wht.png b/res/drawable-finger/dial_num_8_wht.png
new file mode 100644
index 0000000..1954f10
--- /dev/null
+++ b/res/drawable-finger/dial_num_8_wht.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_9.xml b/res/drawable-finger/dial_num_9.xml
new file mode 100644
index 0000000..d0b7fd9
--- /dev/null
+++ b/res/drawable-finger/dial_num_9.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_9_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_9_blk" />
+    <item
+        android:drawable="@drawable/dial_num_9_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_9_blk.png b/res/drawable-finger/dial_num_9_blk.png
new file mode 100644
index 0000000..752df25
--- /dev/null
+++ b/res/drawable-finger/dial_num_9_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_9_wht.png b/res/drawable-finger/dial_num_9_wht.png
new file mode 100644
index 0000000..d8b5aa1
--- /dev/null
+++ b/res/drawable-finger/dial_num_9_wht.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_pound.xml b/res/drawable-finger/dial_num_pound.xml
new file mode 100644
index 0000000..609d748
--- /dev/null
+++ b/res/drawable-finger/dial_num_pound.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_pound_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_pound_blk" />
+    <item
+        android:drawable="@drawable/dial_num_pound_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_pound_blk.png b/res/drawable-finger/dial_num_pound_blk.png
new file mode 100644
index 0000000..4dabda4
--- /dev/null
+++ b/res/drawable-finger/dial_num_pound_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_pound_wht.png b/res/drawable-finger/dial_num_pound_wht.png
new file mode 100644
index 0000000..f27ae87
--- /dev/null
+++ b/res/drawable-finger/dial_num_pound_wht.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_star.xml b/res/drawable-finger/dial_num_star.xml
new file mode 100644
index 0000000..3b3304a
--- /dev/null
+++ b/res/drawable-finger/dial_num_star.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/dial_num_star_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/dial_num_star_blk" />
+    <item
+        android:drawable="@drawable/dial_num_star_wht" />
+</selector>
+
diff --git a/res/drawable-finger/dial_num_star_blk.png b/res/drawable-finger/dial_num_star_blk.png
new file mode 100644
index 0000000..af917ef
--- /dev/null
+++ b/res/drawable-finger/dial_num_star_blk.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_star_wht.png b/res/drawable-finger/dial_num_star_wht.png
new file mode 100644
index 0000000..ffbd43f
--- /dev/null
+++ b/res/drawable-finger/dial_num_star_wht.png
Binary files differ
diff --git a/res/drawable-finger/header_bottom_white.9.png b/res/drawable-finger/header_bottom_white.9.png
new file mode 100644
index 0000000..5d06aec
--- /dev/null
+++ b/res/drawable-finger/header_bottom_white.9.png
Binary files differ
diff --git a/res/drawable-finger/ic_btn_write_sms.png b/res/drawable-finger/ic_btn_write_sms.png
new file mode 100644
index 0000000..0f2cf07
--- /dev/null
+++ b/res/drawable-finger/ic_btn_write_sms.png
Binary files differ
diff --git a/res/drawable-finger/ic_contact_picture.png b/res/drawable-finger/ic_contact_picture.png
new file mode 100644
index 0000000..3a338e8
--- /dev/null
+++ b/res/drawable-finger/ic_contact_picture.png
Binary files differ
diff --git a/res/drawable-finger/ic_contact_picture_2.png b/res/drawable-finger/ic_contact_picture_2.png
new file mode 100644
index 0000000..8b184af
--- /dev/null
+++ b/res/drawable-finger/ic_contact_picture_2.png
Binary files differ
diff --git a/res/drawable-finger/ic_contact_picture_3.png b/res/drawable-finger/ic_contact_picture_3.png
new file mode 100644
index 0000000..a2d08b5
--- /dev/null
+++ b/res/drawable-finger/ic_contact_picture_3.png
Binary files differ
diff --git a/res/drawable-finger/ic_default_number.png b/res/drawable-finger/ic_default_number.png
new file mode 100644
index 0000000..a04e327
--- /dev/null
+++ b/res/drawable-finger/ic_default_number.png
Binary files differ
diff --git a/res/drawable-finger/ic_delete_phone_number.xml b/res/drawable-finger/ic_delete_phone_number.xml
new file mode 100644
index 0000000..adfc0ce
--- /dev/null
+++ b/res/drawable-finger/ic_delete_phone_number.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/ic_delete_phone_number_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/ic_delete_phone_number_blk" />
+    <item
+        android:drawable="@drawable/ic_delete_phone_number_wht" />
+</selector>
+
diff --git a/res/drawable-finger/ic_delete_phone_number_blk.png b/res/drawable-finger/ic_delete_phone_number_blk.png
new file mode 100644
index 0000000..2e83d36
--- /dev/null
+++ b/res/drawable-finger/ic_delete_phone_number_blk.png
Binary files differ
diff --git a/res/drawable-finger/ic_delete_phone_number_wht.png b/res/drawable-finger/ic_delete_phone_number_wht.png
new file mode 100644
index 0000000..988bcfd
--- /dev/null
+++ b/res/drawable-finger/ic_delete_phone_number_wht.png
Binary files differ
diff --git a/res/drawable-finger/ic_dial_number.xml b/res/drawable-finger/ic_dial_number.xml
new file mode 100644
index 0000000..1ba449e
--- /dev/null
+++ b/res/drawable-finger/ic_dial_number.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/ic_dial_number_blk" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/ic_dial_number_blk" />
+    <item
+        android:drawable="@drawable/ic_dial_number_wht" />
+</selector>
+
diff --git a/res/drawable-finger/ic_dial_number_blk.png b/res/drawable-finger/ic_dial_number_blk.png
new file mode 100644
index 0000000..c1f572d
--- /dev/null
+++ b/res/drawable-finger/ic_dial_number_blk.png
Binary files differ
diff --git a/res/drawable-finger/ic_dial_number_wht.png b/res/drawable-finger/ic_dial_number_wht.png
new file mode 100644
index 0000000..d303b2b
--- /dev/null
+++ b/res/drawable-finger/ic_dial_number_wht.png
Binary files differ
diff --git a/res/drawable-finger/ic_menu_allfriends.png b/res/drawable-finger/ic_menu_allfriends.png
new file mode 100755
index 0000000..a5bd331
--- /dev/null
+++ b/res/drawable-finger/ic_menu_allfriends.png
Binary files differ
diff --git a/res/drawable-finger/ic_menu_contact.png b/res/drawable-finger/ic_menu_contact.png
new file mode 100644
index 0000000..29b7cd9
--- /dev/null
+++ b/res/drawable-finger/ic_menu_contact.png
Binary files differ
diff --git a/res/drawable-finger/ic_menu_dialer.png b/res/drawable-finger/ic_menu_dialer.png
new file mode 100644
index 0000000..db21f96
--- /dev/null
+++ b/res/drawable-finger/ic_menu_dialer.png
Binary files differ
diff --git a/res/drawable-finger/ic_menu_send.png b/res/drawable-finger/ic_menu_send.png
new file mode 100644
index 0000000..74c096d
--- /dev/null
+++ b/res/drawable-finger/ic_menu_send.png
Binary files differ
diff --git a/res/drawable-finger/ic_tab_contacts.xml b/res/drawable-finger/ic_tab_contacts.xml
new file mode 100644
index 0000000..3341f41
--- /dev/null
+++ b/res/drawable-finger/ic_tab_contacts.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/ic_tab_selected_contacts" />
+    <item android:drawable="@drawable/ic_tab_unselected_contacts" />
+</selector>
+
diff --git a/res/drawable-finger/ic_tab_dialer.xml b/res/drawable-finger/ic_tab_dialer.xml
new file mode 100644
index 0000000..36115fa
--- /dev/null
+++ b/res/drawable-finger/ic_tab_dialer.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/ic_tab_selected_dialer" />
+    <item android:drawable="@drawable/ic_tab_unselected_dialer" />
+</selector>
+
diff --git a/res/drawable-finger/ic_tab_recent.xml b/res/drawable-finger/ic_tab_recent.xml
new file mode 100644
index 0000000..548d0b7
--- /dev/null
+++ b/res/drawable-finger/ic_tab_recent.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/ic_tab_selected_recent" />
+    <item android:drawable="@drawable/ic_tab_unselected_recent" />
+</selector>
+
diff --git a/res/drawable-finger/ic_tab_selected_contacts.png b/res/drawable-finger/ic_tab_selected_contacts.png
new file mode 100644
index 0000000..16c4a91
--- /dev/null
+++ b/res/drawable-finger/ic_tab_selected_contacts.png
Binary files differ
diff --git a/res/drawable-finger/ic_tab_selected_dialer.png b/res/drawable-finger/ic_tab_selected_dialer.png
new file mode 100644
index 0000000..cb715ed
--- /dev/null
+++ b/res/drawable-finger/ic_tab_selected_dialer.png
Binary files differ
diff --git a/res/drawable-finger/ic_tab_selected_recent.png b/res/drawable-finger/ic_tab_selected_recent.png
new file mode 100644
index 0000000..bdbfc41
--- /dev/null
+++ b/res/drawable-finger/ic_tab_selected_recent.png
Binary files differ
diff --git a/res/drawable-finger/ic_tab_selected_starred.png b/res/drawable-finger/ic_tab_selected_starred.png
new file mode 100644
index 0000000..1b884cb
--- /dev/null
+++ b/res/drawable-finger/ic_tab_selected_starred.png
Binary files differ
diff --git a/res/drawable-finger/ic_tab_starred.xml b/res/drawable-finger/ic_tab_starred.xml
new file mode 100644
index 0000000..e84db4a
--- /dev/null
+++ b/res/drawable-finger/ic_tab_starred.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/ic_tab_selected_starred" />
+    <item android:drawable="@drawable/ic_tab_unselected_starred" />
+</selector>
+
diff --git a/res/drawable-finger/ic_tab_unselected_contacts.png b/res/drawable-finger/ic_tab_unselected_contacts.png
new file mode 100644
index 0000000..7b5b84f
--- /dev/null
+++ b/res/drawable-finger/ic_tab_unselected_contacts.png
Binary files differ
diff --git a/res/drawable-finger/ic_tab_unselected_dialer.png b/res/drawable-finger/ic_tab_unselected_dialer.png
new file mode 100644
index 0000000..dc3c5d8
--- /dev/null
+++ b/res/drawable-finger/ic_tab_unselected_dialer.png
Binary files differ
diff --git a/res/drawable-finger/ic_tab_unselected_recent.png b/res/drawable-finger/ic_tab_unselected_recent.png
new file mode 100644
index 0000000..712e405
--- /dev/null
+++ b/res/drawable-finger/ic_tab_unselected_recent.png
Binary files differ
diff --git a/res/drawable-finger/ic_tab_unselected_starred.png b/res/drawable-finger/ic_tab_unselected_starred.png
new file mode 100644
index 0000000..8eaccb8
--- /dev/null
+++ b/res/drawable-finger/ic_tab_unselected_starred.png
Binary files differ
diff --git a/res/drawable-finger/star_on.png b/res/drawable-finger/star_on.png
new file mode 100644
index 0000000..429dada
--- /dev/null
+++ b/res/drawable-finger/star_on.png
Binary files differ
diff --git a/res/drawable-finger/sym_action_call.png b/res/drawable-finger/sym_action_call.png
new file mode 100644
index 0000000..058ec4b
--- /dev/null
+++ b/res/drawable-finger/sym_action_call.png
Binary files differ
diff --git a/res/drawable-finger/sym_action_chat.png b/res/drawable-finger/sym_action_chat.png
new file mode 100644
index 0000000..88bfa0d
--- /dev/null
+++ b/res/drawable-finger/sym_action_chat.png
Binary files differ
diff --git a/res/drawable-finger/sym_action_email.png b/res/drawable-finger/sym_action_email.png
new file mode 100644
index 0000000..9110bc6
--- /dev/null
+++ b/res/drawable-finger/sym_action_email.png
Binary files differ
diff --git a/res/drawable-finger/sym_action_map.png b/res/drawable-finger/sym_action_map.png
new file mode 100644
index 0000000..ab7a70c
--- /dev/null
+++ b/res/drawable-finger/sym_action_map.png
Binary files differ
diff --git a/res/drawable-finger/sym_action_sms.png b/res/drawable-finger/sym_action_sms.png
new file mode 100644
index 0000000..df9603f
--- /dev/null
+++ b/res/drawable-finger/sym_action_sms.png
Binary files differ
diff --git a/res/drawable/dialog_full_dark.9.png b/res/drawable/dialog_full_dark.9.png
new file mode 100755
index 0000000..18914b3
--- /dev/null
+++ b/res/drawable/dialog_full_dark.9.png
Binary files differ
diff --git a/res/drawable/ic_launcher_contacts.png b/res/drawable/ic_launcher_contacts.png
new file mode 100644
index 0000000..826656f
--- /dev/null
+++ b/res/drawable/ic_launcher_contacts.png
Binary files differ
diff --git a/res/drawable/ic_launcher_phone.png b/res/drawable/ic_launcher_phone.png
new file mode 100644
index 0000000..4e613ec
--- /dev/null
+++ b/res/drawable/ic_launcher_phone.png
Binary files differ
diff --git a/res/layout-finger/contacts_list_content.xml b/res/layout-finger/contacts_list_content.xml
new file mode 100644
index 0000000..23f8521
--- /dev/null
+++ b/res/layout-finger/contacts_list_content.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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="fill_parent"
+        android:layout_height="fill_parent"
+        android:orientation="vertical">
+<!-- The group banner isn't used anymore
+    <TextView 
+            android:id="@+id/contact_group"
+            android:text="@string/noContacts"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:background="@android:drawable/title_bar"
+            style="@android:style/TextAppearance.WindowTitle"/>
+-->
+    <com.android.contacts.FastScrollView 
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent">
+        <ListView android:id="@android:id/list"
+                android:layout_width="fill_parent" 
+                android:layout_height="fill_parent"
+        />
+
+        <TextView android:id="@android:id/empty"
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent"
+                android:gravity="center"
+                android:text="@string/noContacts"
+                android:textAppearance="?android:attr/textAppearanceLarge"
+        />
+    </com.android.contacts.FastScrollView>
+</LinearLayout>
diff --git a/res/layout-finger/contacts_list_item.xml b/res/layout-finger/contacts_list_item.xml
new file mode 100644
index 0000000..3d3b58c
--- /dev/null
+++ b/res/layout-finger/contacts_list_item.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingLeft="5dip"
+    android:orientation="vertical"
+>
+    <TextView android:id="@+id/name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="6dip"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:layout_alignParentTop="true"
+    />
+
+    <TextView android:id="@+id/label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="2dip"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textStyle="bold"
+        android:layout_below="@id/name"
+    />
+
+    <TextView android:id="@+id/number"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="5dip"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:layout_toRightOf="@id/label"
+        android:layout_alignBaseline="@id/label"
+    />
+</RelativeLayout>
diff --git a/res/layout-finger/dialer_activity.xml b/res/layout-finger/dialer_activity.xml
new file mode 100644
index 0000000..242821b
--- /dev/null
+++ b/res/layout-finger/dialer_activity.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+
+<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/tabhost"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+
+        <TabWidget android:id="@android:id/tabs"
+            android:layout_width="fill_parent"
+            android:layout_height="68dip"
+            android:paddingLeft="1dip"
+            android:paddingRight="1dip"
+            android:paddingTop="4dip"
+        />
+
+        <FrameLayout android:id="@android:id/tabcontent"
+            android:layout_width="fill_parent"
+            android:layout_height="0dip"
+            android:layout_weight="1"
+        />
+    </LinearLayout>
+</TabHost>
+
diff --git a/res/layout-finger/dialpad.xml b/res/layout-finger/dialpad.xml
new file mode 100644
index 0000000..30018f0
--- /dev/null
+++ b/res/layout-finger/dialpad.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+
+<com.android.contacts.ButtonGridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/dialpad"
+    android:paddingLeft="16px"
+    android:paddingRight="16px"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+>
+        <ImageButton android:id="@+id/one"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_1"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+            
+        <ImageButton android:id="@+id/two"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_2"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+
+        <ImageButton android:id="@+id/three"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_3"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+
+        <ImageButton android:id="@+id/four"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_4"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+        
+        <ImageButton android:id="@+id/five"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_5"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+
+        <ImageButton android:id="@+id/six"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_6"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+
+        <ImageButton android:id="@+id/seven"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_7"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+    
+        <ImageButton android:id="@+id/eight"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_8"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+
+        <ImageButton android:id="@+id/nine"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_9"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+
+        <ImageButton android:id="@+id/star"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_star"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+    
+        <ImageButton android:id="@+id/zero"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_0"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+
+        <ImageButton android:id="@+id/pound"
+            android:layout_width="96px"
+            android:layout_height="76px"
+            android:src="@drawable/dial_num_pound"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+        />
+</com.android.contacts.ButtonGridLayout>
diff --git a/res/layout-finger/edit_contact.xml b/res/layout-finger/edit_contact.xml
new file mode 100644
index 0000000..858d654
--- /dev/null
+++ b/res/layout-finger/edit_contact.xml
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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"
+    android:orientation="vertical"
+    android:scrollbarStyle="insideInset"
+    android:fillViewport="true">
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:orientation="vertical"
+    >
+
+        <LinearLayout android:id="@+id/banner"
+            android:layout_width="fill_parent"
+            android:layout_height="?android:attr/listPreferredItemHeight"
+            android:orientation="horizontal"
+            android:background="@android:drawable/bottom_bar"
+            android:padding="0dip"
+            android:gravity="center_vertical"
+            android:baselineAligned="false"
+        >
+
+            <FrameLayout
+                android:layout_width="58dip"
+                android:layout_height="58dip"
+                android:layout_marginLeft="6dip"
+                android:layout_marginRight="10dip"
+            >
+                <Button android:id="@+id/photoButton"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    android:text="@string/addPicture"
+                    android:gravity="center"
+                />
+
+                <ImageView android:id="@+id/photoImage"
+                    style="?android:attr/imageWellStyle"
+                    android:layout_width="fill_parent"
+                    android:layout_height="fill_parent"
+                    android:clickable="true"
+                    android:focusable="true"
+                    android:background="@drawable/border"
+                />
+            </FrameLayout>
+
+            <EditText android:id="@+id/name"
+                android:layout_width="0dip"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="4dip"
+                android:gravity="center_vertical"
+                android:singleLine="true"
+                android:capitalize="words"
+                android:hint="@string/ghostData_name"
+                android:nextFocusDown="@id/data"
+            />
+
+            <ImageView android:id="@+id/star"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dip"
+            android:background="?android:attr/listDivider"
+        />
+
+        <LinearLayout
+            android:layout_width="fill_parent"
+            android:layout_height="0dip"
+            android:layout_weight="1"
+            android:orientation="vertical"
+        >
+
+            <!-- The edit items -->
+            <LinearLayout android:id="@+id/list"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+            />
+
+            <!-- Send to voicemail checkbox -->
+            <RelativeLayout
+                android:layout_width="fill_parent"
+                android:layout_height="?android:attr/listPreferredItemHeight"
+            >
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_centerVertical="true"
+                    android:layout_alignParentLeft="true"
+                    android:layout_marginLeft="4dip"
+                    android:text="@string/send_to_voicemail_checkbox"
+                    android:textAppearance="?android:attr/textAppearanceMedium"
+                />
+
+                <CheckBox android:id="@+id/send_to_voicemail"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_centerVertical="true"
+                    android:layout_alignParentRight="true"
+                    android:nextFocusDown="@+id/saveButton"
+                />
+
+            </RelativeLayout>
+
+            <View
+                android:layout_width="fill_parent"
+                android:layout_height="1dip"
+                android:background="?android:attr/listDivider"
+            />
+
+            <!-- The add more button -->
+            <Button android:id="@+id/addMore"
+                android:layout_width="fill_parent"
+                android:layout_height="?android:attr/listPreferredItemHeight"
+                android:layout_marginTop="3dip"
+                android:textAppearance="?android:attr/textAppearanceLarge"
+                android:text="@string/menu_addItem"
+                android:gravity="left|center_vertical"
+                android:drawableLeft="@android:drawable/ic_input_add"
+            />
+
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="fill_parent"
+            android:layout_height="?android:attr/listPreferredItemHeight"
+            android:layout_marginTop="1dip"
+            android:background="@android:drawable/bottom_bar"
+        >
+            <Button android:id="@+id/saveButton"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:layout_alignParentLeft="true"
+                android:text="@string/button_save"
+                android:minWidth="120dip"
+                android:minHeight="48dip"
+            />
+
+            <Button android:id="@+id/discardButton"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:layout_alignParentRight="true"
+                android:text="@string/button_discard"
+                android:minWidth="120dip"
+                android:minHeight="48dip"
+            />
+        </RelativeLayout>
+    </LinearLayout>
+</ScrollView>
diff --git a/res/layout-finger/edit_contact_entry.xml b/res/layout-finger/edit_contact_entry.xml
new file mode 100644
index 0000000..eb01782
--- /dev/null
+++ b/res/layout-finger/edit_contact_entry.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+>
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeight"
+        android:orientation="horizontal"
+        android:baselineAligned="false"
+        android:gravity="center_vertical"
+    >
+
+        <Button android:id="@+id/label"
+            android:layout_width="100dip"
+            android:layout_height="wrap_content"
+            android:gravity="left|center_vertical"
+        />
+
+        <EditText android:id="@+id/data"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="8dip"
+        />
+
+        <ImageButton android:id="@+id/delete"
+            android:src="@android:drawable/ic_delete"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+        />
+    </LinearLayout>
+
+    <View
+        android:layout_width="fill_parent"
+        android:layout_height="1dip"
+        android:background="@android:drawable/divider_horizontal_dark"
+    />
+</LinearLayout>
diff --git a/res/layout-finger/edit_contact_entry_org.xml b/res/layout-finger/edit_contact_entry_org.xml
new file mode 100644
index 0000000..f29696b
--- /dev/null
+++ b/res/layout-finger/edit_contact_entry_org.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+>
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:baselineAligned="false"
+        android:gravity="center_vertical"
+    >
+
+        <Button android:id="@+id/label"
+            android:layout_width="100dip"
+            android:layout_height="wrap_content"
+            android:gravity="left|center_vertical"
+        />
+
+        <LinearLayout
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+        >
+            <LinearLayout
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                android:baselineAligned="false"
+                android:gravity="center_vertical"
+            >
+
+                <EditText android:id="@+id/data"
+                    android:layout_width="0dip"
+                    android:layout_weight="1"
+                    android:layout_height="wrap_content"
+                    android:layout_marginLeft="8dip"
+                />
+
+                <ImageButton android:id="@+id/delete"
+                    android:src="@android:drawable/ic_delete"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                android:baselineAligned="false"
+                android:gravity="center_vertical"
+            >
+
+                <EditText android:id="@+id/data2"
+                    android:layout_width="0dip"
+                    android:layout_weight="1"
+                    android:layout_height="wrap_content"
+                    android:layout_marginLeft="8dip"
+                />
+
+                <ImageButton android:id="@+id/delete2"
+                    android:src="@android:drawable/ic_delete"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                />
+            </LinearLayout>
+        </LinearLayout>
+    </LinearLayout>
+
+    <View
+        android:layout_width="fill_parent"
+        android:layout_height="1dip"
+        android:background="@android:drawable/divider_horizontal_dark"
+    />
+</LinearLayout>
diff --git a/res/layout-finger/edit_contact_entry_ringtone.xml b/res/layout-finger/edit_contact_entry_ringtone.xml
new file mode 100644
index 0000000..e566e31
--- /dev/null
+++ b/res/layout-finger/edit_contact_entry_ringtone.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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="fill_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="3dip"
+    android:orientation="vertical"
+>
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="?android:attr/listPreferredItemHeight"
+        android:layout_marginTop="2dip"
+        android:orientation="horizontal"
+        android:baselineAligned="false"
+        android:gravity="center_vertical"
+    >
+
+        <TextView android:id="@+id/label"
+            android:layout_width="102dip"
+            android:layout_height="fill_parent"
+            android:paddingLeft="4dip"
+            android:gravity="left|center_vertical"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+        />
+
+        <Button android:id="@+id/data"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceLargeInverse"
+            android:baselineAligned="false"
+            android:background="@android:drawable/btn_dropdown"
+            android:gravity="center_vertical"
+            android:maxLines="1"
+        />
+        
+    </LinearLayout>
+
+    <View
+        android:layout_width="fill_parent"
+        android:layout_height="1dip"
+        android:background="@android:drawable/divider_horizontal_dark"
+    />
+</LinearLayout>
diff --git a/res/layout-finger/edit_contact_entry_static_label.xml b/res/layout-finger/edit_contact_entry_static_label.xml
new file mode 100644
index 0000000..830872a
--- /dev/null
+++ b/res/layout-finger/edit_contact_entry_static_label.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+>
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeight"
+        android:orientation="horizontal"
+        android:baselineAligned="false"
+        android:gravity="center_vertical"
+    >
+
+        <TextView android:id="@+id/label"
+            android:layout_width="102dip"
+            android:layout_height="fill_parent"
+            android:paddingLeft="4dip"
+            android:gravity="left|center_vertical"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+        />
+
+        <EditText android:id="@+id/data"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="8dip"
+        />
+
+        <ImageButton android:id="@+id/delete"
+            android:src="@android:drawable/ic_delete"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+        />
+    </LinearLayout>
+
+    <View
+        android:layout_width="fill_parent"
+        android:layout_height="1dip"
+        android:background="@android:drawable/divider_horizontal_dark"
+    />
+</LinearLayout>
diff --git a/res/layout-finger/edit_separator.xml b/res/layout-finger/edit_separator.xml
new file mode 100644
index 0000000..57bb53a
--- /dev/null
+++ b/res/layout-finger/edit_separator.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<!-- Layout used for edit separators. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+>
+
+    <TextView android:id="@+id/text"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:background="@drawable/dark_header_dithered"
+        android:textColor="?android:attr/textColorSecondary"
+        android:textStyle="bold"
+        android:textSize="14sp"
+        android:paddingLeft="8dip"
+    />
+
+    <View
+        android:layout_width="fill_parent"
+        android:layout_height="1dip"
+        android:background="?android:attr/listDivider"
+    />
+
+</LinearLayout>
+
diff --git a/res/layout-finger/list_separator.xml b/res/layout-finger/list_separator.xml
new file mode 100644
index 0000000..e6f75ab
--- /dev/null
+++ b/res/layout-finger/list_separator.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<!-- Layout used for list separators. -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:background="@drawable/dark_header_dithered"
+    android:textColor="?android:attr/textColorSecondary"
+    android:textStyle="bold"
+    android:textSize="14sp"
+    android:paddingLeft="8dip"
+/>
diff --git a/res/layout-finger/recent_calls.xml b/res/layout-finger/recent_calls.xml
new file mode 100644
index 0000000..986d913
--- /dev/null
+++ b/res/layout-finger/recent_calls.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+>
+    <ListView android:id="@android:id/list"
+        android:layout_width="fill_parent" 
+        android:layout_height="fill_parent"
+        android:scrollbarStyle="outsideOverlay"
+    />
+    
+    <TextView android:id="@android:id/empty"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:text="@string/recentCalls_empty"
+        android:gravity="center"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+    />
+</FrameLayout>
diff --git a/res/layout-finger/recent_calls_list_item.xml b/res/layout-finger/recent_calls_list_item.xml
new file mode 100644
index 0000000..3df1f13
--- /dev/null
+++ b/res/layout-finger/recent_calls_list_item.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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">
+
+    <ImageView android:id="@+id/call_type_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="4dip"
+        android:layout_centerVertical="true"
+    />
+
+    <TextView android:id="@+id/line1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/call_type_icon"
+        android:layout_marginTop="6dip"
+        android:layout_marginLeft="5dip"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:singleLine="true"
+    />
+
+    <TextView android:id="@+id/line2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/line1"
+        android:layout_alignLeft="@id/line1"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:singleLine="true"
+    />
+    
+    <TextView android:id="@+id/duration"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_alignBaseline="@id/line1"
+        android:layout_toRightOf="@id/line1"
+        android:layout_marginRight="8dip"
+        android:gravity="right"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textStyle="bold"
+        android:textColor="?android:attr/textColorPrimary"
+        android:singleLine="true"
+    />
+    
+    <TextView android:id="@+id/date"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_alignBaseline="@id/line2"
+        android:layout_marginRight="8dip"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:singleLine="true"
+    />
+</RelativeLayout>
diff --git a/res/layout-finger/twelve_key_dialer.xml b/res/layout-finger/twelve_key_dialer.xml
new file mode 100644
index 0000000..84f2517
--- /dev/null
+++ b/res/layout-finger/twelve_key_dialer.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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:id="@+id/top"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical"
+>
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="66px"
+        android:layout_marginTop="3px"
+        android:layout_marginBottom="5px"
+        android:layout_marginLeft="3px"
+        android:layout_marginRight="3px"
+        android:orientation="horizontal"
+    >
+
+        <EditText android:id="@+id/digits"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="66px"
+            android:maxLines="1"
+            android:scrollHorizontally="true"
+            android:textSize="28sp"
+            android:freezesText="true"
+            android:background="@drawable/btn_dial_textfield"
+            android:textColor="@color/dialer_button_text"
+            android:focusableInTouchMode="false"
+            android:nextFocusRight="@+id/backspace"
+        />
+
+        <ImageButton android:id="@+id/backspace"
+            style="@android:style/Widget.Button.Inset"
+            android:layout_width="wrap_content"
+            android:layout_height="66px"
+            android:background="@drawable/btn_dial_delete"
+            android:src="@drawable/ic_delete_phone_number"
+            android:gravity="center"
+        />
+
+    </LinearLayout>
+
+    <!-- Keypad section -->
+    <include layout="@layout/dialpad" />
+</LinearLayout>
diff --git a/res/layout-finger/view_contact.xml b/res/layout-finger/view_contact.xml
new file mode 100644
index 0000000..23393a4
--- /dev/null
+++ b/res/layout-finger/view_contact.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical"
+>
+
+    <LinearLayout android:id="@+id/banner"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:background="@android:drawable/title_bar"
+        android:padding="0dip"
+        android:gravity="center_vertical"
+    >
+
+        <ImageView android:id="@+id/photo"
+            style="?android:attr/imageWellStyle"
+            android:layout_width="84dip"
+            android:layout_height="84dip"
+            android:layout_marginRight="10dip"
+            android:scaleType="fitCenter"
+            android:background="@drawable/border"
+        />
+
+        <TextView android:id="@+id/name"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:shadowColor="#BB000000"
+            android:shadowRadius="2.75"
+        />
+
+        <CheckBox android:id="@+id/star"
+            style="?android:attr/starStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+        />
+
+    </LinearLayout>
+
+    <ListView android:id="@android:id/list"
+        android:layout_width="fill_parent" 
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:layout_marginTop="1dip"
+        android:scrollbarStyle="outsideOverlay"
+    />
+</LinearLayout>
diff --git a/res/layout-finger/view_contact_entry.xml b/res/layout-finger/view_contact_entry.xml
new file mode 100644
index 0000000..772c7d9
--- /dev/null
+++ b/res/layout-finger/view_contact_entry.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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="fill_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="horizontal"
+>
+
+    <TextView android:id="@+id/label"
+        android:layout_width="72dip"
+        android:layout_height="fill_parent"
+        android:layout_marginLeft="8dip"
+        android:layout_marginRight="10dip"
+        android:gravity="center_vertical"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+    />
+
+    <TextView android:id="@+id/data"
+        android:layout_width="0px"
+        android:layout_height="fill_parent"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:paddingRight="6dip"
+    />
+</LinearLayout>
diff --git a/res/layout-finger/view_contact_entry_only_label.xml b/res/layout-finger/view_contact_entry_only_label.xml
new file mode 100644
index 0000000..0cfa3c7
--- /dev/null
+++ b/res/layout-finger/view_contact_entry_only_label.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/label"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:paddingLeft="8dip"
+    android:paddingRight="10dip"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:gravity="center_vertical"
+    android:singleLine="true"
+    android:textAppearance="?android:attr/textAppearanceSmall"
+/>
diff --git a/res/layout-finger/view_contact_entry_organization.xml b/res/layout-finger/view_contact_entry_organization.xml
new file mode 100644
index 0000000..bd80786
--- /dev/null
+++ b/res/layout-finger/view_contact_entry_organization.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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="fill_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="horizontal"
+>
+
+    <TextView android:id="@+id/label"
+        android:layout_width="72dip"
+        android:layout_height="fill_parent"
+        android:layout_marginLeft="8dip"
+        android:layout_marginRight="10dip"
+        android:gravity="center_vertical"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+    />
+
+    <ImageView android:id="@+id/preferredIcon"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:layout_marginRight="8dip"
+        android:src="@drawable/ic_default_number"
+        android:visibility="gone"
+        android:gravity="center_vertical"
+    />
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:orientation="vertical"
+        android:gravity="center_vertical"
+    >
+        <TextView android:id="@+id/data"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+        />
+
+        <TextView android:id="@+id/data2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+        />
+    </LinearLayout>
+</LinearLayout>
diff --git a/res/layout-land-finger/twelve_key_dialer.xml b/res/layout-land-finger/twelve_key_dialer.xml
new file mode 100644
index 0000000..3daf6d2
--- /dev/null
+++ b/res/layout-land-finger/twelve_key_dialer.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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:id="@+id/top"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical"
+>
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="66px"
+        android:addStatesFromChildren="true"
+        android:gravity="center_vertical"
+        android:baselineAligned="false"
+        android:layout_marginTop="3px"
+        android:layout_marginBottom="5px"
+        android:layout_marginLeft="3px"
+        android:layout_marginRight="3px"
+    >
+
+        <EditText android:id="@+id/digits"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="66px"
+            android:maxLines="1"
+            android:scrollHorizontally="true"
+            android:textSize="28sp"
+            android:freezesText="true"
+            android:background="@drawable/btn_dial_textfield"
+            android:textColor="@color/dialer_button_text"
+            android:hint="@string/dialerKeyboardHintText"
+        />
+
+        <!--
+            The button goes at the right.
+        -->
+        <ImageButton android:id="@+id/backspace"
+            style="@android:style/Widget.Button.Inset"
+            android:src="@drawable/ic_delete_phone_number"
+            android:layout_width="wrap_content"
+            android:layout_height="66px"
+            android:background="@drawable/btn_dial_delete"
+            android:gravity="center"
+        />
+
+    </LinearLayout>
+</LinearLayout>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
new file mode 100644
index 0000000..ac39f7d
--- /dev/null
+++ b/res/values-cs/strings.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="attachToContact">Obrázek kontaktu</string>
+  <string name="button_discard">Zrušit změny</string>
+  <string name="button_save">Uložit</string>
+  <string name="cancelButtonText">Storno</string>
+  <string name="contactsList">Kontakty</string>
+  <string name="continueEditing">Pokračovat v úpravách</string>
+  <string name="customLabelPickerTitle">Vlastní štítek</string>
+  <string name="deleteConfirmation">Odstranit tento kontakt?</string>
+  <string name="discardChanges">Zrušit změny</string>
+  <string name="displayAllPreference">Zobrazit všechny kontakty</string>
+  <string name="displayAllPreference_summary">Zobrazit všechny kontakty nebo jen kontakty s telefonními čísly</string>
+  <string name="editContact_title_edit">Upravit kontakt</string>
+  <string name="editContact_title_insert">Nový kontakt</string>
+  <string name="emailLabelsGroup">E-mail</string>
+  <string name="errorDialogTitle">Upozornění!</string>
+  <string name="frequentList">Časté</string>
+  <string name="ghostData_company">Společnost</string>
+  <string name="ghostData_email">E-mailová adresa</string>
+  <string name="ghostData_im">Jméno pro zasílání rychlých zpráv</string>
+  <string name="ghostData_name">První a poslední</string>
+  <string name="ghostData_notes">Poznámka</string>
+  <string name="ghostData_phone">Telefonní číslo</string>
+  <string name="ghostData_postal">"Ulice\nMěsto, stát, PSČ\nZemě"</string>
+  <string name="ghostData_title">Poloha</string>
+  <string name="imLabelsGroup">Rych. zpr.</string>
+  <string name="importFromSim">Importovat z karty SIM</string>
+  <string name="invalidContactMessage">Kontakty musí mít názvy</string>
+  <string name="label_company">Společnost</string>
+  <string name="label_notes">Poznámky</string>
+  <string name="label_title">Funkce</string>
+  <string name="menu_addItem">a další</string>
+  <string name="menu_addStar">Přidat hvězdičku</string>
+  <string name="menu_call">Volat</string>
+  <string name="menu_callNumber">Volat<xliff:g id="contact">%s</xliff:g></string>
+  <string name="menu_deleteContact">Odstranit kontakt</string>
+  <string name="menu_dialer">Telefon</string>
+  <string name="menu_doNotSave">Zrušit změny</string>
+  <string name="menu_done">Uložit</string>
+  <string name="menu_editContact">Upravit kontakt</string>
+  <string name="menu_editNumberBeforeCall">Upravit číslo před voláním</string>
+  <string name="menu_makeDefaultNumber">Vytvořit výchozí číslo</string>
+  <string name="menu_newContact">Nový kontakt</string>
+  <string name="menu_preferences">Nastavení</string>
+  <string name="menu_removeStar">Odebrat hvězdičku</string>
+  <string name="menu_sendEmail">Odeslat e-mail</string>
+  <string name="menu_sendSMS">Odeslat text</string>
+  <string name="menu_viewAddress">Adresa na mapě</string>
+  <string name="menu_viewContact">Zobrazit kontakt</string>
+  <string name="noButton">Storno</string>
+  <string name="okButtonText">OK</string>
+  <string name="otherLabelsGroup">Jiné</string>
+  <string name="phoneLabelsGroup">Telefon</string>
+  <string name="photoPickerNotFoundText">Žádné obrázky nejsou k dispozici.</string>
+  <string name="pickerNewContactHeader">Vytvořit nový kontakt</string>
+  <string name="postalLabelsGroup">Poštovní adresa</string>
+  <string name="searchHint">Hledat v kontaktech</string>
+  <string name="selectLabel">Vybrat štítek</string>
+  <string name="sms_entry_label">Odeslat SMS</string>
+  <string name="starredList">Hlavní</string>
+  <string name="strequentList">Oblíbené položky</string>
+  <string name="yesButton">OK</string>
+</resources>
diff --git a/res/values-de-rDE/strings.xml b/res/values-de-rDE/strings.xml
new file mode 100644
index 0000000..dc2aa59
--- /dev/null
+++ b/res/values-de-rDE/strings.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="addPicture">Symbol hinzufügen</string>
+  <string name="allContacts">Alle Kontakte</string>
+  <string name="alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
+  <string name="attachToContact">Kontaktsymbol</string>
+  <string name="button_discard">Änderungen verwerfen</string>
+  <string name="button_save">Speichern</string>
+  <string name="cancelButtonText">Abbrechen</string>
+  <string name="contactCreatedToast">Kontakt erstellt.</string>
+  <string name="contactSavedToast">Kontakt gespeichert.</string>
+  <string name="contactsFavoritesLabel">Favoriten</string>
+  <string name="contactsIconLabel">Kontakte</string>
+  <string name="contactsList">Kontakte</string>
+  <string name="customLabelPickerTitle">Benutzerdefiniertes Etikett</string>
+  <string name="default_ringtone">Standard</string>
+  <string name="deleteConfirmation">Dieser Kontakt wird gelöscht.</string>
+  <string name="deleteConfirmation_title">Löschen</string>
+  <string name="dialerIconLabel">Wähltastatur</string>
+  <string name="dialerKeyboardHintText">Feste Tastatur zum Wählen verwenden</string>
+  <string name="doneButton">Fertig</string>
+  <string name="editContactDescription">Kontakt bearbeiten</string>
+  <string name="editContact_title_edit">Kontakt bearbeiten</string>
+  <string name="editContact_title_insert">Neuer Kontakt</string>
+  <string name="emailLabelsGroup">E-Mail</string>
+  <string name="errorDialogTitle">Keine Bilder</string>
+  <string name="frequentList">Häufig</string>
+  <string name="ghostData_company">Firma</string>
+  <string name="ghostData_email">E-Mail-Adresse</string>
+  <string name="ghostData_im">IM-Name</string>
+  <string name="ghostData_name">Vor- und Nachname</string>
+  <string name="ghostData_notes">Eigene Notiz</string>
+  <string name="ghostData_phone">Telefonnummer</string>
+  <string name="ghostData_postal">Postadresse</string>
+  <string name="ghostData_title">Position</string>
+  <string name="groupEmpty">Die \"<xliff:g id="groupName">%s</xliff:g>\" Gruppe ist leer.</string>
+  <string name="groupNameMyContacts">Eigene Kontakte</string>
+  <string name="groupNameWithPhones">Kontakte mit Telefonnummern</string>
+  <string name="imLabelsGroup">IM</string>
+  <string name="imei">IMEI</string>
+  <string name="importFromSim">SIM-Kontakt-Import</string>
+  <string name="insertContactDescription">Kontakt erstellen</string>
+  <string name="invalidContactMessage">Der Kontakt existiert nicht.</string>
+  <string name="label_company">Firma</string>
+  <string name="label_notes">Notizen</string>
+  <string name="label_ringtone">Klingelton</string>
+  <string name="label_title">Arbeitsbezeichnung</string>
+  <string name="listSeparatorCallNumber">Nummer wählen</string>
+  <string name="listSeparatorMapAddress">Kartenansicht Adresse</string>
+  <string name="listSeparatorOrganizations">Organisationen</string>
+  <string name="listSeparatorOtherInformation">Andere Informationen</string>
+  <string name="listSeparatorSendEmail">E-Mail senden</string>
+  <string name="listSeparatorSendIm">Sofortnachricht senden</string>
+  <string name="listSeparatorSendSmsMms">SMS/MMS senden</string>
+  <string name="menu_addItem">Weitere Info</string>
+  <string name="menu_addStar">Zu Favoriten hinzufügen</string>
+  <string name="menu_call">Anrufen</string>
+  <string name="menu_callNumber"><xliff:g id="contact">%s</xliff:g> anrufen</string>
+  <string name="menu_deleteContact">Kontakt löschen</string>
+  <string name="menu_dialer">Wähltastatur</string>
+  <string name="menu_displayGroup">Gruppe anzeigen</string>
+  <string name="menu_doNotSave">Änderungen verwerfen</string>
+  <string name="menu_done">Speichern</string>
+  <string name="menu_editContact">Kontakt bearbeiten</string>
+  <string name="menu_editNumberBeforeCall">Nummer vor Anruf bearbeiten</string>
+  <string name="menu_makeDefaultNumber">Standardnummer erstellen</string>
+  <string name="menu_newContact">Neuer Kontakt</string>
+  <string name="menu_newFavortie">Neuer Favorit</string>
+  <string name="menu_preferences">Einstellungen</string>
+  <string name="menu_removeStar">Aus Favoriten entfernen</string>
+  <string name="menu_sendEmail">E-Mail senden</string>
+  <string name="menu_sendSMS">SMS/MMS senden</string>
+  <string name="menu_sendTextMessage">SMS-Nachricht senden</string>
+  <string name="menu_viewAddress">Kartenansicht Adresse</string>
+  <string name="menu_viewContact">Kontakt anzeigen</string>
+  <string name="noButton">Abbrechen</string>
+  <string name="noContacts">Keine Kontakte.</string>
+  <string name="noContactsWithPhoneNumbers">Keine Kontakte mit Telefonnummern.</string>
+  <string name="noFavorites">Keine Favoriten.</string>
+  <string name="no_imei">Unbekannte IMEI</string>
+  <string name="ok">OK</string>
+  <string name="okButtonText">OK</string>
+  <string name="otherLabelsGroup">Andere</string>
+  <string name="phoneIconLabel">Telefon</string>
+  <string name="phoneLabelsGroup">Telefon</string>
+  <string name="photoPickerNotFoundText">Für Telefon stehen keine Bilder zur Verfügung.</string>
+  <string name="pickerNewContactHeader">Neuen Kontakt erstellen</string>
+  <string name="postalLabelsGroup">Postanschrift</string>
+  <string name="private_num">Privatnummer</string>
+  <string name="recentCallsIconLabel">Anrufliste</string>
+  <string name="recentCalls_addToContact">Zu Kontakten hinzufügen</string>
+  <string name="recentCalls_callNumber"><xliff:g id="who">%s</xliff:g> anrufen</string>
+  <string name="recentCalls_deleteAll">Anrufliste löschen</string>
+  <string name="recentCalls_editNumberBeforeCall">Nummer vor Anruf bearbeiten</string>
+  <string name="recentCalls_empty">Anrufliste ist leer.</string>
+  <string name="recentCalls_removeFromRecentList">Aus Anrufliste entfernen</string>
+  <string name="removePicture">Symbol entfernen</string>
+  <string name="searchHint">Kontakte suchen</string>
+  <string name="selectGroupsToSync">Gruppen synchronisieren</string>
+  <string name="selectLabel">Etikett auswählen</string>
+  <string name="select_group_title">Gruppe zur Anzeige auswählen</string>
+  <string name="send_to_voicemail_checkbox">Anrufe direkt zur Mobilbox suchen.</string>
+  <string name="send_to_voicemail_view">Anrufe werden direkt zur Mobilbox gesendet.</string>
+  <string name="shortcutContact">Kontakt</string>
+  <string name="showAllGroups">Alle Kontakte</string>
+  <string name="showingGroupTitle">
+					<xliff:g id="groupName">%s</xliff:g>
+				</string>
+  <string name="simContacts_emptyLoading">Laden von SIM-Karte\u2026</string>
+  <string name="simContacts_title">SIM-Karten-Kontakte</string>
+  <string name="sms_entry_label">SMS/MMS senden</string>
+  <string name="starredList">Mit Stern</string>
+  <string name="strequentList">Favoriten</string>
+  <string name="syncAllGroups">Alle Kontakte synchronisieren</string>
+  <string name="syncGroupChooserTitle">Gruppe für Synchronisierung auswählen</string>
+  <string name="syncGroupPreference">Gruppen synchronisieren</string>
+  <string name="syncGroupPreference_summary">Gruppen für Synchronisierung auswählen</string>
+  <string name="unknown">Unbekannt</string>
+  <string name="viewContactDesription">Kontakt anzeigen</string>
+  <string name="viewContactTitle">Kontaktdetails</string>
+  <string name="voicemail">Mobilbox</string>
+  <string name="yesButton">OK</string>
+</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..fdd1323
--- /dev/null
+++ b/res/values-en-rGB/strings.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="addPicture">Add icon</string>
+  <string name="allContacts">All contacts</string>
+  <string name="alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
+  <string name="attachToContact">Contact icon</string>
+  <string name="button_discard">Discard changes</string>
+  <string name="button_save">Save</string>
+  <string name="cancelButtonText">Cancel</string>
+  <string name="contactCreatedToast">Contact created.</string>
+  <string name="contactSavedToast">Contact saved.</string>
+  <string name="contactsFavoritesLabel">Favourites</string>
+  <string name="contactsIconLabel">Contacts</string>
+  <string name="contactsList">Contacts</string>
+  <string name="customLabelPickerTitle">Custom label</string>
+  <string name="default_ringtone">Default</string>
+  <string name="deleteConfirmation">This contact will be deleted.</string>
+  <string name="deleteConfirmation_title">Delete</string>
+  <string name="dialerIconLabel">Dialer</string>
+  <string name="dialerKeyboardHintText">Use keyboard to dial</string>
+  <string name="doneButton">Done</string>
+  <string name="editContactDescription">Edit contact</string>
+  <string name="editContact_title_edit">Edit contact</string>
+  <string name="editContact_title_insert">New contact</string>
+  <string name="emailLabelsGroup">Email</string>
+  <string name="errorDialogTitle">No pictures</string>
+  <string name="frequentList">Frequent</string>
+  <string name="ghostData_company">Company</string>
+  <string name="ghostData_email">Email address</string>
+  <string name="ghostData_im">IM name</string>
+  <string name="ghostData_name">First and Last</string>
+  <string name="ghostData_notes">My note</string>
+  <string name="ghostData_phone">Phone number</string>
+  <string name="ghostData_postal">Postal address</string>
+  <string name="ghostData_title">Position</string>
+  <string name="groupEmpty">Your \"<xliff:g id="groupName">%s</xliff:g>\" group is empty.</string>
+  <string name="groupNameMyContacts">My Contacts</string>
+  <string name="groupNameWithPhones">Contacts with phone numbers</string>
+  <string name="imLabelsGroup">IM</string>
+  <string name="imei">IMEI</string>
+  <string name="importFromSim">Import from SIM card</string>
+  <string name="insertContactDescription">Create contact</string>
+  <string name="invalidContactMessage">The contact does not exist.</string>
+  <string name="label_company">Company</string>
+  <string name="label_notes">Notes</string>
+  <string name="label_ringtone">Ringtone</string>
+  <string name="label_title">Job title</string>
+  <string name="listSeparatorCallNumber">Dial number</string>
+  <string name="listSeparatorMapAddress">Map address</string>
+  <string name="listSeparatorOrganizations">Organisations</string>
+  <string name="listSeparatorOtherInformation">Other information</string>
+  <string name="listSeparatorSendEmail">Send email</string>
+  <string name="listSeparatorSendIm">Send instant message</string>
+  <string name="listSeparatorSendSmsMms">Send SMS/MMS</string>
+  <string name="menu_addItem">More info</string>
+  <string name="menu_addStar">Add to favourites</string>
+  <string name="menu_call">Call</string>
+  <string name="menu_callNumber">Call <xliff:g id="contact">%s</xliff:g></string>
+  <string name="menu_deleteContact">Delete contact</string>
+  <string name="menu_dialer">Dialer</string>
+  <string name="menu_displayGroup">Display group</string>
+  <string name="menu_doNotSave">Discard changes</string>
+  <string name="menu_done">Save</string>
+  <string name="menu_editContact">Edit contact</string>
+  <string name="menu_editNumberBeforeCall">Edit number before call</string>
+  <string name="menu_makeDefaultNumber">Make default number</string>
+  <string name="menu_newContact">New contact</string>
+  <string name="menu_newFavortie">New favourite</string>
+  <string name="menu_preferences">Settings</string>
+  <string name="menu_removeStar">Remove from favourites</string>
+  <string name="menu_sendEmail">Send email</string>
+  <string name="menu_sendSMS">Send SMS/MMS</string>
+  <string name="menu_sendTextMessage">Send SMS message</string>
+  <string name="menu_viewAddress">Map address</string>
+  <string name="menu_viewContact">View contact</string>
+  <string name="noButton">Cancel</string>
+  <string name="noContacts">No contacts.</string>
+  <string name="noContactsWithPhoneNumbers">No contacts with phone numbers.</string>
+  <string name="noFavorites">No favourites.</string>
+  <string name="no_imei">Unknown IMEI</string>
+  <string name="ok">OK</string>
+  <string name="okButtonText">OK</string>
+  <string name="otherLabelsGroup">Other</string>
+  <string name="phoneIconLabel">Phone</string>
+  <string name="phoneLabelsGroup">Phone</string>
+  <string name="photoPickerNotFoundText">No pictures are available on the phone.</string>
+  <string name="pickerNewContactHeader">Create new contact</string>
+  <string name="postalLabelsGroup">Postal address</string>
+  <string name="private_num">Private number</string>
+  <string name="recentCallsIconLabel">Call log</string>
+  <string name="recentCalls_addToContact">Add to contacts</string>
+  <string name="recentCalls_callNumber">Call <xliff:g id="who">%s</xliff:g></string>
+  <string name="recentCalls_deleteAll">Clear call log</string>
+  <string name="recentCalls_editNumberBeforeCall">Edit number before call</string>
+  <string name="recentCalls_empty">Call log is empty.</string>
+  <string name="recentCalls_removeFromRecentList">Remove from call log</string>
+  <string name="removePicture">Remove icon</string>
+  <string name="searchHint">Search contacts</string>
+  <string name="selectGroupsToSync">Sync groups</string>
+  <string name="selectLabel">Select label</string>
+  <string name="select_group_title">Select group to display</string>
+  <string name="send_to_voicemail_checkbox">Send calls directly to voicemail.</string>
+  <string name="send_to_voicemail_view">Calls are sent directly to voicemail.</string>
+  <string name="shortcutContact">Contact</string>
+  <string name="showAllGroups">All contacts</string>
+  <string name="showingGroupTitle"><xliff:g id="groupName">%s</xliff:g></string>
+  <string name="simContacts_emptyLoading">Loading from SIM card\u2026</string>
+  <string name="simContacts_title">SIM card contacts</string>
+  <string name="sms_entry_label">Send SMS/MMS</string>
+  <string name="starredList">Starred</string>
+  <string name="strequentList">Favourites</string>
+  <string name="syncAllGroups">Sync all contacts</string>
+  <string name="syncGroupChooserTitle">Select groups to sync</string>
+  <string name="syncGroupPreference">Sync groups</string>
+  <string name="syncGroupPreference_summary">Select groups to synchronize</string>
+  <string name="unknown">Unknown</string>
+  <string name="viewContactDesription">View contact</string>
+  <string name="viewContactTitle">Contact details</string>
+  <string name="voicemail">Voicemail</string>
+  <string name="yesButton">OK</string>
+</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..db7617e
--- /dev/null
+++ b/res/values-es-rUS/strings.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="addPicture">Agregar icono</string>
+  <string name="allContacts">Todos los contactos</string>
+  <string name="alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
+  <string name="attachToContact">Icono de Contacto</string>
+  <string name="button_discard">Rechazar cambios</string>
+  <string name="button_save">Guardar</string>
+  <string name="cancelButtonText">Cancelar</string>
+  <string name="contactCreatedToast">Contacto creado.</string>
+  <string name="contactSavedToast">Contacto guardado.</string>
+  <string name="contactsFavoritesLabel">Favoritos</string>
+  <string name="contactsIconLabel">Contactos</string>
+  <string name="contactsList">Contactos</string>
+  <string name="customLabelPickerTitle">Etiqueta personalizada</string>
+  <string name="default_ringtone">Predeterminado</string>
+  <string name="deleteConfirmation">Se eliminará este contacto.</string>
+  <string name="deleteConfirmation_title">Eliminar</string>
+  <string name="dialerIconLabel">Marcador</string>
+  <string name="dialerKeyboardHintText">Utilizar teclado para marcar</string>
+  <string name="doneButton">Listo</string>
+  <string name="editContactDescription">Editar contacto</string>
+  <string name="editContact_title_edit">Editar contacto</string>
+  <string name="editContact_title_insert">Nuevo contacto</string>
+  <string name="emailLabelsGroup">Correo electrónico</string>
+  <string name="errorDialogTitle">Ninguna imagen</string>
+  <string name="frequentList">Frecuente</string>
+  <string name="ghostData_company">Organización</string>
+  <string name="ghostData_email">Dirección de correo electrónico</string>
+  <string name="ghostData_im">Nombre MI</string>
+  <string name="ghostData_name">Nombre y apellido</string>
+  <string name="ghostData_notes">Mi nota</string>
+  <string name="ghostData_phone">Número de teléfono</string>
+  <string name="ghostData_postal">Dirección postal</string>
+  <string name="ghostData_title">Puesto</string>
+  <string name="groupEmpty">Su \"<xliff:g id="groupName">%s</xliff:g>\" grupo está vacío.</string>
+  <string name="groupNameMyContacts">Mis contactos</string>
+  <string name="groupNameWithPhones">Contactos con números de teléfono</string>
+  <string name="imLabelsGroup">MI</string>
+  <string name="imei">IMEI</string>
+  <string name="importFromSim">Importador de contactos de la SIM</string>
+  <string name="insertContactDescription">Crear contacto</string>
+  <string name="invalidContactMessage">El contacto no existe. </string>
+  <string name="label_company">Organización</string>
+  <string name="label_notes">Notas</string>
+  <string name="label_ringtone">Tono de timbre</string>
+  <string name="label_title">Título</string>
+  <string name="listSeparatorCallNumber">Marcar número</string>
+  <string name="listSeparatorMapAddress">Dirección en el mapa</string>
+  <string name="listSeparatorOrganizations">Organizaciones</string>
+  <string name="listSeparatorOtherInformation">Otra información</string>
+  <string name="listSeparatorSendEmail">Enviar correo electrónico</string>
+  <string name="listSeparatorSendIm">Enviar mensaje instantáneo</string>
+  <string name="listSeparatorSendSmsMms">Enviar SMS/MMS</string>
+  <string name="menu_addItem">Más información</string>
+  <string name="menu_addStar">Agregar a favoritos</string>
+  <string name="menu_call">Llamar</string>
+  <string name="menu_callNumber">Llamar a <xliff:g id="contact">%s</xliff:g></string>
+  <string name="menu_deleteContact">Eliminar contacto</string>
+  <string name="menu_dialer">Marcador</string>
+  <string name="menu_displayGroup">Mostrar grupo</string>
+  <string name="menu_doNotSave">Rechazar cambios</string>
+  <string name="menu_done">Guardar</string>
+  <string name="menu_editContact">Editar contacto</string>
+  <string name="menu_editNumberBeforeCall">Editar número antes de llamar</string>
+  <string name="menu_makeDefaultNumber">Convertir en número predeterminado</string>
+  <string name="menu_newContact">Nuevo contacto</string>
+  <string name="menu_newFavortie">Nuevo favorito</string>
+  <string name="menu_preferences">Configuración</string>
+  <string name="menu_removeStar">Quitar de favoritos</string>
+  <string name="menu_sendEmail">Enviar correo electrónico</string>
+  <string name="menu_sendSMS">Enviar SMS/MMS</string>
+  <string name="menu_sendTextMessage">Enviar mensaje SMS </string>
+  <string name="menu_viewAddress">Dirección en el mapa</string>
+  <string name="menu_viewContact">Ver contacto</string>
+  <string name="noButton">Cancelar</string>
+  <string name="noContacts">No hay contactos. </string>
+  <string name="noContactsWithPhoneNumbers">Sin contactos con números de teléfono. </string>
+  <string name="noFavorites">Sin favoritos.</string>
+  <string name="no_imei">IMEI desconocido</string>
+  <string name="ok">Aceptar</string>
+  <string name="okButtonText">Aceptar</string>
+  <string name="otherLabelsGroup">Otros</string>
+  <string name="phoneIconLabel">Teléfono</string>
+  <string name="phoneLabelsGroup">Teléfono</string>
+  <string name="photoPickerNotFoundText">No se dispone de imágenes en el teléfono. </string>
+  <string name="pickerNewContactHeader">Crear nuevo contacto</string>
+  <string name="postalLabelsGroup">Dirección postal</string>
+  <string name="private_num">Número privado</string>
+  <string name="recentCallsIconLabel">Registro de llamadas</string>
+  <string name="recentCalls_addToContact">Agregar a contactos</string>
+  <string name="recentCalls_callNumber">Llamar a <xliff:g id="who">%s</xliff:g></string>
+  <string name="recentCalls_deleteAll">Borrar registro de llamadas</string>
+  <string name="recentCalls_editNumberBeforeCall">Editar número antes de llamar</string>
+  <string name="recentCalls_empty">El registro de la llamada está vacío.</string>
+  <string name="recentCalls_removeFromRecentList">Eliminar del registro de llamadas</string>
+  <string name="removePicture">Eliminar icono</string>
+  <string name="searchHint">Buscar contactos</string>
+  <string name="selectGroupsToSync">Grupos de sincronización</string>
+  <string name="selectLabel">Seleccionar etiqueta</string>
+  <string name="select_group_title">Seleccionar grupo a visualizar</string>
+  <string name="send_to_voicemail_checkbox">Enviar las llamadas directamente al correo de voz.</string>
+  <string name="send_to_voicemail_view">Las llamadas se envían directamente al correo de voz.</string>
+  <string name="shortcutContact">Contacto</string>
+  <string name="showAllGroups">Todos los contactos</string>
+  <string name="showingGroupTitle">
+					<xliff:g id="groupName">%s</xliff:g>
+				</string>
+  <string name="simContacts_emptyLoading">Cargando de tarjeta SIM\u2026</string>
+  <string name="simContacts_title">Contactos de la tarjeta SIM</string>
+  <string name="sms_entry_label">Enviar SMS/MMS</string>
+  <string name="starredList">Con estrella</string>
+  <string name="strequentList">Favoritos</string>
+  <string name="syncAllGroups">Sincronizar todos los contactos</string>
+  <string name="syncGroupChooserTitle">Seleccionar grupos a sincronizar</string>
+  <string name="syncGroupPreference">Grupos de sincronización</string>
+  <string name="syncGroupPreference_summary">Seleccionar grupos a sincronizar</string>
+  <string name="unknown">Desconocido</string>
+  <string name="viewContactDesription">Ver contacto</string>
+  <string name="viewContactTitle">Detalles del contacto</string>
+  <string name="voicemail">Correo de voz</string>
+  <string name="yesButton">Aceptar</string>
+</resources>
diff --git a/res/values-fr-rFR/strings.xml b/res/values-fr-rFR/strings.xml
new file mode 100644
index 0000000..649348f
--- /dev/null
+++ b/res/values-fr-rFR/strings.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="addPicture">Ajouter une icône</string>
+  <string name="allContacts">Tous les contacts</string>
+  <string name="alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
+  <string name="attachToContact">Icône de contact</string>
+  <string name="button_discard">Abandonner les modifications</string>
+  <string name="button_save">Enregistrer</string>
+  <string name="cancelButtonText">Annuler</string>
+  <string name="contactCreatedToast">Contact créé.</string>
+  <string name="contactSavedToast">Contact enregistré.</string>
+  <string name="contactsFavoritesLabel">Favoris</string>
+  <string name="contactsIconLabel">Contacts</string>
+  <string name="contactsList">Contacts</string>
+  <string name="customLabelPickerTitle">Étiquette personnalisée</string>
+  <string name="default_ringtone">Par défaut</string>
+  <string name="deleteConfirmation">Ce contact sera supprimé.</string>
+  <string name="deleteConfirmation_title">Supprimer</string>
+  <string name="dialerIconLabel">Numéroteur</string>
+  <string name="dialerKeyboardHintText">Utiliser le clavier pour numéroter</string>
+  <string name="doneButton">Terminé</string>
+  <string name="editContactDescription">Modifier le contact</string>
+  <string name="editContact_title_edit">Modifier le contact</string>
+  <string name="editContact_title_insert">Nouveau contact</string>
+  <string name="emailLabelsGroup">Email</string>
+  <string name="errorDialogTitle">Aucune image</string>
+  <string name="frequentList">Fréquent</string>
+  <string name="ghostData_company">Société</string>
+  <string name="ghostData_email">Adresse de messagerie</string>
+  <string name="ghostData_im">Nom IM</string>
+  <string name="ghostData_name">Prénom et Nom</string>
+  <string name="ghostData_notes">Ma note</string>
+  <string name="ghostData_phone">Numéro de téléphone</string>
+  <string name="ghostData_postal">Adresse postale</string>
+  <string name="ghostData_title">Position</string>
+  <string name="groupEmpty">Votre groupe \"<xliff:g id="groupName">%s</xliff:g>\" est vide.</string>
+  <string name="groupNameMyContacts">Mes contacts</string>
+  <string name="groupNameWithPhones">Contacts avec numéros de téléphone</string>
+  <string name="imLabelsGroup">IM</string>
+  <string name="imei">IMEI</string>
+  <string name="importFromSim">Importateur de contacts SIM</string>
+  <string name="insertContactDescription">Créer un contact</string>
+  <string name="invalidContactMessage">Le contact n\'existe pas.</string>
+  <string name="label_company">Société</string>
+  <string name="label_notes">Notes</string>
+  <string name="label_ringtone">Sonnerie</string>
+  <string name="label_title">Titre</string>
+  <string name="listSeparatorCallNumber">Composer le numéro</string>
+  <string name="listSeparatorMapAddress">Adresse carte</string>
+  <string name="listSeparatorOrganizations">Organisations</string>
+  <string name="listSeparatorOtherInformation">Autres informations</string>
+  <string name="listSeparatorSendEmail">Envoyer e-mail</string>
+  <string name="listSeparatorSendIm">Envoyer message instantané</string>
+  <string name="listSeparatorSendSmsMms">Envoyer SMS/MMS</string>
+  <string name="menu_addItem">Plus d\'infos</string>
+  <string name="menu_addStar">Ajouter aux favoris</string>
+  <string name="menu_call">Appeler</string>
+  <string name="menu_callNumber">Appeler <xliff:g id="contact">%s</xliff:g></string>
+  <string name="menu_deleteContact">Supprimer le contact</string>
+  <string name="menu_dialer">Numéroteur</string>
+  <string name="menu_displayGroup">Afficher le groupe</string>
+  <string name="menu_doNotSave">Abandonner les modifications</string>
+  <string name="menu_done">Enregistrer</string>
+  <string name="menu_editContact">Modifier le contact</string>
+  <string name="menu_editNumberBeforeCall">Modifier le numéro avant l\'appel</string>
+  <string name="menu_makeDefaultNumber">Définir numéro par défaut</string>
+  <string name="menu_newContact">Nouveau contact</string>
+  <string name="menu_newFavortie">Nouveau favori</string>
+  <string name="menu_preferences">Paramètres</string>
+  <string name="menu_removeStar">Supprimer des favoris</string>
+  <string name="menu_sendEmail">Envoyer e-mail</string>
+  <string name="menu_sendSMS">Envoyer SMS/MMS</string>
+  <string name="menu_sendTextMessage">Envoyer SMS</string>
+  <string name="menu_viewAddress">Adresse carte</string>
+  <string name="menu_viewContact">Afficher le contact</string>
+  <string name="noButton">Annuler</string>
+  <string name="noContacts">Aucun contact.</string>
+  <string name="noContactsWithPhoneNumbers">Aucun contact avec numéros de téléphone.</string>
+  <string name="noFavorites">Aucun favori.</string>
+  <string name="no_imei">IMEI inconnu</string>
+  <string name="ok">OK</string>
+  <string name="okButtonText">OK</string>
+  <string name="otherLabelsGroup">Autre</string>
+  <string name="phoneIconLabel">Téléphone</string>
+  <string name="phoneLabelsGroup">Téléphone</string>
+  <string name="photoPickerNotFoundText">Aucune image disponible sur le téléphone.</string>
+  <string name="pickerNewContactHeader">Créer un nouveau contact</string>
+  <string name="postalLabelsGroup">Adresse postale</string>
+  <string name="private_num">Numéro privé</string>
+  <string name="recentCallsIconLabel">Journal d\'appel</string>
+  <string name="recentCalls_addToContact">Ajouter aux contacts</string>
+  <string name="recentCalls_callNumber">Appeler <xliff:g id="who">%s</xliff:g></string>
+  <string name="recentCalls_deleteAll">Effacer le journal d\'appel</string>
+  <string name="recentCalls_editNumberBeforeCall">Modifier le numéro avant l\'appel</string>
+  <string name="recentCalls_empty">Le journal d\'appel est vide.</string>
+  <string name="recentCalls_removeFromRecentList">Supprimer du journal d\'appel</string>
+  <string name="removePicture">Supprimer l\'icône</string>
+  <string name="searchHint">Rechercher les contacts</string>
+  <string name="selectGroupsToSync">Synchroniser les groupes</string>
+  <string name="selectLabel">Sélectionner étiquette</string>
+  <string name="select_group_title">Sélectionner un groupe à afficher</string>
+  <string name="send_to_voicemail_checkbox">Envoyez les appels directement à la messagerie.</string>
+  <string name="send_to_voicemail_view">Les appels sont envoyés directement à la messagerie.</string>
+  <string name="shortcutContact">Contact</string>
+  <string name="showAllGroups">Tous les contacts</string>
+  <string name="showingGroupTitle">
+					<xliff:g id="groupName">%s</xliff:g>
+				</string>
+  <string name="simContacts_emptyLoading">Chargement depuis la carte SIM\u2026</string>
+  <string name="simContacts_title">Contacts de la carte SIM</string>
+  <string name="sms_entry_label">Envoyer SMS/MMS</string>
+  <string name="starredList">Étoilé</string>
+  <string name="strequentList">Favoris</string>
+  <string name="syncAllGroups">Synchroniser tous les contacts</string>
+  <string name="syncGroupChooserTitle">Sélectionner les groupes à synchroniser</string>
+  <string name="syncGroupPreference">Synchroniser les groupes</string>
+  <string name="syncGroupPreference_summary">Sélectionner les groupes à synchroniser</string>
+  <string name="unknown">Inconnu</string>
+  <string name="viewContactDesription">Afficher le contact</string>
+  <string name="viewContactTitle">Détails du contact</string>
+  <string name="voicemail">Messagerie vocale</string>
+  <string name="yesButton">OK</string>
+</resources>
diff --git a/res/values-it-rIT/strings.xml b/res/values-it-rIT/strings.xml
new file mode 100644
index 0000000..24e3e17
--- /dev/null
+++ b/res/values-it-rIT/strings.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="addPicture">Aggiungi icona</string>
+  <string name="allContacts">Tutti i contatti</string>
+  <string name="alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
+  <string name="attachToContact">Icona Contatti</string>
+  <string name="button_discard">Ignora modifiche</string>
+  <string name="button_save">Salva</string>
+  <string name="cancelButtonText">Annulla</string>
+  <string name="contactCreatedToast">Creazione contatto completata.</string>
+  <string name="contactSavedToast">Salvataggio contatto completato.</string>
+  <string name="contactsFavoritesLabel">Preferiti</string>
+  <string name="contactsIconLabel">Contatti</string>
+  <string name="contactsList">Contatti</string>
+  <string name="customLabelPickerTitle">Etichetta personalizzata</string>
+  <string name="default_ringtone">Predefinito</string>
+  <string name="deleteConfirmation">Il contatto verrà eliminato.</string>
+  <string name="deleteConfirmation_title">Elimina</string>
+  <string name="dialerIconLabel">Dialer</string>
+  <string name="dialerKeyboardHintText">Utilizzare la tastiera per comporre il numero</string>
+  <string name="doneButton">Completato</string>
+  <string name="editContactDescription">Modifica contatto</string>
+  <string name="editContact_title_edit">Modifica contatto</string>
+  <string name="editContact_title_insert">Nuovo contatto</string>
+  <string name="emailLabelsGroup">Email</string>
+  <string name="errorDialogTitle">Nessuna immagine</string>
+  <string name="frequentList">Frequente</string>
+  <string name="ghostData_company">Società</string>
+  <string name="ghostData_email">Indirizzo e-mail</string>
+  <string name="ghostData_im">Nome IM</string>
+  <string name="ghostData_name">Nome e cognome</string>
+  <string name="ghostData_notes">Nota</string>
+  <string name="ghostData_phone">Numero di telefono</string>
+  <string name="ghostData_postal">Indirizzo postale</string>
+  <string name="ghostData_title">Posizione</string>
+  <string name="groupEmpty">Il gruppo \"<xliff:g id="groupName">%s</xliff:g>\" è vuoto.</string>
+  <string name="groupNameMyContacts">Contatti</string>
+  <string name="groupNameWithPhones">Contatti con numeri di telefono</string>
+  <string name="imLabelsGroup">IM</string>
+  <string name="imei">IMEI</string>
+  <string name="importFromSim">Importazione contati SIM</string>
+  <string name="insertContactDescription">Crea contatto</string>
+  <string name="invalidContactMessage">Il contatto non esiste.</string>
+  <string name="label_company">Società</string>
+  <string name="label_notes">Note</string>
+  <string name="label_ringtone">Suoneria</string>
+  <string name="label_title">Posizione</string>
+  <string name="listSeparatorCallNumber">Componi numero</string>
+  <string name="listSeparatorMapAddress">Indirizzo su cartina</string>
+  <string name="listSeparatorOrganizations">Organizzazioni</string>
+  <string name="listSeparatorOtherInformation">Altre informazioni</string>
+  <string name="listSeparatorSendEmail">Invia messaggio e-mail</string>
+  <string name="listSeparatorSendIm">Invia messaggio immediato</string>
+  <string name="listSeparatorSendSmsMms">Invia SMS/MMS</string>
+  <string name="menu_addItem">Ulteriori informazioni</string>
+  <string name="menu_addStar">Aggiungi a Preferiti</string>
+  <string name="menu_call">Chiama</string>
+  <string name="menu_callNumber">Chiama <xliff:g id="contact">%s</xliff:g></string>
+  <string name="menu_deleteContact">Elimina contatto</string>
+  <string name="menu_dialer">Dialer</string>
+  <string name="menu_displayGroup">Visualizza gruppo</string>
+  <string name="menu_doNotSave">Ignora modifiche</string>
+  <string name="menu_done">Salva</string>
+  <string name="menu_editContact">Modifica contatto</string>
+  <string name="menu_editNumberBeforeCall">Modifica numero prima di chiamare</string>
+  <string name="menu_makeDefaultNumber">Imposta come numero predefinito</string>
+  <string name="menu_newContact">Nuovo contatto</string>
+  <string name="menu_newFavortie">Nuovo preferito</string>
+  <string name="menu_preferences">Impostazioni</string>
+  <string name="menu_removeStar">Rimuovi da Preferiti</string>
+  <string name="menu_sendEmail">Invia messaggio e-mail</string>
+  <string name="menu_sendSMS">Invia SMS/MMS</string>
+  <string name="menu_sendTextMessage">Invia messaggio SMS</string>
+  <string name="menu_viewAddress">Indirizzo su cartina</string>
+  <string name="menu_viewContact">Visualizza contatto</string>
+  <string name="noButton">Annulla</string>
+  <string name="noContacts">Nessun contatto.</string>
+  <string name="noContactsWithPhoneNumbers">Nessun contatto con numeri di telefono.</string>
+  <string name="noFavorites">Nessun preferito.</string>
+  <string name="no_imei">IMEI sconosciuto</string>
+  <string name="ok">OK</string>
+  <string name="okButtonText">OK</string>
+  <string name="otherLabelsGroup">Altro</string>
+  <string name="phoneIconLabel">Telefono</string>
+  <string name="phoneLabelsGroup">Telefono</string>
+  <string name="photoPickerNotFoundText">Nessuna immagine disponibile sul telefono.</string>
+  <string name="pickerNewContactHeader">Crea nuovo contatto</string>
+  <string name="postalLabelsGroup">Indirizzo postale</string>
+  <string name="private_num">Numero privato</string>
+  <string name="recentCallsIconLabel">Registro chiamate</string>
+  <string name="recentCalls_addToContact">Aggiungi a Contatti</string>
+  <string name="recentCalls_callNumber">Chiama <xliff:g id="who">%s</xliff:g></string>
+  <string name="recentCalls_deleteAll">Cancella registro chiamate</string>
+  <string name="recentCalls_editNumberBeforeCall">Modifica numero prima di chiamare</string>
+  <string name="recentCalls_empty">Il registro chiamate è vuoto.</string>
+  <string name="recentCalls_removeFromRecentList">Rimuovi da registro chiamate</string>
+  <string name="removePicture">Icona Rimuovi</string>
+  <string name="searchHint">Cerca contatti</string>
+  <string name="selectGroupsToSync">Sincronizza gruppi</string>
+  <string name="selectLabel">Seleziona etichetta</string>
+  <string name="select_group_title">Seleziona gruppo da visualizzare</string>
+  <string name="send_to_voicemail_checkbox">Invia le chiamate direttamente alla casella vocale.</string>
+  <string name="send_to_voicemail_view">Le chiamate vengono inviate direttamente alla casella vocale.</string>
+  <string name="shortcutContact">Contatto</string>
+  <string name="showAllGroups">Tutti i contatti</string>
+  <string name="showingGroupTitle">
+					<xliff:g id="groupName">%s</xliff:g>
+				</string>
+  <string name="simContacts_emptyLoading">Caricamento dalla scheda SIM in corso\u2026</string>
+  <string name="simContacts_title">Contatti scheda SIM</string>
+  <string name="sms_entry_label">Invia SMS/MMS</string>
+  <string name="starredList">Primo piano</string>
+  <string name="strequentList">Preferiti</string>
+  <string name="syncAllGroups">Sincronizza tutti i contatti</string>
+  <string name="syncGroupChooserTitle">Selezionare i gruppi da sincronizzare</string>
+  <string name="syncGroupPreference">Sincronizza gruppi</string>
+  <string name="syncGroupPreference_summary">Selezionare i gruppi da sincronizzare</string>
+  <string name="unknown">Sconosciuto</string>
+  <string name="viewContactDesription">Visualizza contatto</string>
+  <string name="viewContactTitle">Dettagli contatto</string>
+  <string name="voicemail">Posta vocale</string>
+  <string name="yesButton">OK</string>
+</resources>
diff --git a/res/values-nl-rNL/strings.xml b/res/values-nl-rNL/strings.xml
new file mode 100644
index 0000000..9955277
--- /dev/null
+++ b/res/values-nl-rNL/strings.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="attachToContact">Afbeelding contactpersoon </string>
+  <string name="button_discard">Wijzigingen negeren</string>
+  <string name="button_save">Opslaan</string>
+  <string name="cancelButtonText">Annuleren</string>
+  <string name="contactsList">Contacten</string>
+  <string name="continueEditing">Bewerken hervatten</string>
+  <string name="customLabelPickerTitle">Aangepast label</string>
+  <string name="deleteConfirmation">Deze contact verwijderen?</string>
+  <string name="discardChanges">Wijzigingen negeren</string>
+  <string name="displayAllPreference">Alle contacten weergeven</string>
+  <string name="displayAllPreference_summary">Alle contacten weergeven, of alleen die met telefoonnummers</string>
+  <string name="editContact_title_edit">Contact bewerken</string>
+  <string name="editContact_title_insert">Nieuw contact</string>
+  <string name="emailLabelsGroup">E-mail</string>
+  <string name="errorDialogTitle">Let op!</string>
+  <string name="frequentList">Regelmatig</string>
+  <string name="ghostData_company">Bedrijf</string>
+  <string name="ghostData_email">E-mailadres</string>
+  <string name="ghostData_im">IM-naam</string>
+  <string name="ghostData_name">Eerste en laatste</string>
+  <string name="ghostData_notes">Mijn opmerking</string>
+  <string name="ghostData_phone">Telefoonnummer</string>
+  <string name="ghostData_postal">"Straat\nPlaats, staat postcode\nland"</string>
+  <string name="ghostData_title">Positie</string>
+  <string name="imLabelsGroup">Expresbericht</string>
+  <string name="importFromSim">Importeren van sim-kaart</string>
+  <string name="invalidContactMessage">Contacten moeten namen hebben</string>
+  <string name="label_company">Bedrijf</string>
+  <string name="label_notes">Opmerkingen</string>
+  <string name="label_title">Functie</string>
+  <string name="menu_addItem">Meer toevoegen</string>
+  <string name="menu_addStar">Ster toevoegen</string>
+  <string name="menu_call">Bellen</string>
+  <string name="menu_callNumber">Bellen<xliff:g id="contact">%s</xliff:g></string>
+  <string name="menu_deleteContact">Contact verwijderen</string>
+  <string name="menu_dialer">Kiezer</string>
+  <string name="menu_doNotSave">Wijzigingen negeren</string>
+  <string name="menu_done">Opslaan</string>
+  <string name="menu_editContact">Contact bewerken</string>
+  <string name="menu_editNumberBeforeCall">Nummer bewerken voor bellen</string>
+  <string name="menu_makeDefaultNumber">Standaardnummer maken</string>
+  <string name="menu_newContact">Nieuw contact</string>
+  <string name="menu_preferences">Instellingen</string>
+  <string name="menu_removeStar">Ster verwijderen</string>
+  <string name="menu_sendEmail">E-mail verzenden</string>
+  <string name="menu_sendSMS">Tekst verzenden</string>
+  <string name="menu_viewAddress">Kaartadres</string>
+  <string name="menu_viewContact">Contact weergeven</string>
+  <string name="noButton">Annuleren</string>
+  <string name="okButtonText">OK</string>
+  <string name="otherLabelsGroup">Overig</string>
+  <string name="phoneLabelsGroup">Telefoon</string>
+  <string name="photoPickerNotFoundText">Geen afbeeldingen beschikbaar.</string>
+  <string name="pickerNewContactHeader">Nieuwe contact maken</string>
+  <string name="postalLabelsGroup">Postadres</string>
+  <string name="searchHint">Contacten zoeken</string>
+  <string name="selectLabel">Label selecteren</string>
+  <string name="sms_entry_label">SMS verzenden</string>
+  <string name="starredList">Met ster</string>
+  <string name="strequentList">Favorieten</string>
+  <string name="yesButton">OK</string>
+</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..8d527f5
--- /dev/null
+++ b/res/values-zh-rTW/strings.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="addPicture">新增圖示</string>
+  <string name="allContacts">所有連絡人</string>
+  <string name="alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
+  <string name="attachToContact">連絡人圖示</string>
+  <string name="button_discard">放棄變更</string>
+  <string name="button_save">儲存</string>
+  <string name="cancelButtonText">取消</string>
+  <string name="contactCreatedToast">已經建立連絡人。</string>
+  <string name="contactSavedToast">已經儲存連絡人。</string>
+  <string name="contactsFavoritesLabel">我的最愛</string>
+  <string name="contactsIconLabel">連絡人</string>
+  <string name="contactsList">連絡人</string>
+  <string name="customLabelPickerTitle">自訂標籤</string>
+  <string name="default_ringtone">預設值</string>
+  <string name="deleteConfirmation">將會刪除此連絡人。</string>
+  <string name="deleteConfirmation_title">刪除</string>
+  <string name="dialerIconLabel">撥號員</string>
+  <string name="dialerKeyboardHintText">使用鍵盤撥號</string>
+  <string name="doneButton">完成</string>
+  <string name="editContactDescription">編輯連絡人</string>
+  <string name="editContact_title_edit">編輯連絡人</string>
+  <string name="editContact_title_insert">新增連絡人</string>
+  <string name="emailLabelsGroup">電子郵件</string>
+  <string name="errorDialogTitle">無圖片</string>
+  <string name="frequentList">經常</string>
+  <string name="ghostData_company">公司</string>
+  <string name="ghostData_email">電子郵件地址</string>
+  <string name="ghostData_im">立即訊息名稱</string>
+  <string name="ghostData_name">姓名</string>
+  <string name="ghostData_notes">我的記事</string>
+  <string name="ghostData_phone">電話號碼</string>
+  <string name="ghostData_postal">郵遞地址</string>
+  <string name="ghostData_title">職位</string>
+  <string name="groupEmpty">\"<xliff:g id="groupName">%s</xliff:g>\" 群組空白。</string>
+  <string name="groupNameMyContacts">我的連絡人</string>
+  <string name="groupNameWithPhones">含有電話號碼的連絡人</string>
+  <string name="imLabelsGroup">立即訊息</string>
+  <string name="imei">IMEI</string>
+  <string name="importFromSim">SIM 卡連絡人匯入工具</string>
+  <string name="insertContactDescription">建立連絡人</string>
+  <string name="invalidContactMessage">此連絡人不存在。</string>
+  <string name="label_company">公司</string>
+  <string name="label_notes">記事</string>
+  <string name="label_ringtone">響鈴音調</string>
+  <string name="label_title">職稱</string>
+  <string name="listSeparatorCallNumber">撥號</string>
+  <string name="listSeparatorMapAddress">地圖地址</string>
+  <string name="listSeparatorOrganizations">組織</string>
+  <string name="listSeparatorOtherInformation">其它資訊</string>
+  <string name="listSeparatorSendEmail">傳送電子郵件</string>
+  <string name="listSeparatorSendIm">傳送立即訊息</string>
+  <string name="listSeparatorSendSmsMms">傳送簡訊/多媒體簡訊</string>
+  <string name="menu_addItem">其它資訊</string>
+  <string name="menu_addStar">加到我的最愛</string>
+  <string name="menu_call">撥號</string>
+  <string name="menu_callNumber">撥打到 <xliff:g id="contact">%s</xliff:g></string>
+  <string name="menu_deleteContact">刪除連絡人</string>
+  <string name="menu_dialer">撥號員</string>
+  <string name="menu_displayGroup">顯示群組</string>
+  <string name="menu_doNotSave">放棄變更</string>
+  <string name="menu_done">儲存</string>
+  <string name="menu_editContact">編輯連絡人</string>
+  <string name="menu_editNumberBeforeCall">撥號之前編輯號碼</string>
+  <string name="menu_makeDefaultNumber">設成預設號碼</string>
+  <string name="menu_newContact">新增連絡人</string>
+  <string name="menu_newFavortie">新增我的最愛</string>
+  <string name="menu_preferences">設定</string>
+  <string name="menu_removeStar">從我的最愛中移除</string>
+  <string name="menu_sendEmail">傳送電子郵件</string>
+  <string name="menu_sendSMS">傳送簡訊/多媒體簡訊</string>
+  <string name="menu_sendTextMessage">傳送簡訊</string>
+  <string name="menu_viewAddress">地圖地址</string>
+  <string name="menu_viewContact">檢視連絡人</string>
+  <string name="noButton">取消</string>
+  <string name="noContacts">無連絡人。</string>
+  <string name="noContactsWithPhoneNumbers">無含有電話號碼的連絡人。</string>
+  <string name="noFavorites">無我的最愛。</string>
+  <string name="no_imei">未知的 IMEI</string>
+  <string name="ok">確定</string>
+  <string name="okButtonText">確定</string>
+  <string name="otherLabelsGroup">其他</string>
+  <string name="phoneIconLabel">電話</string>
+  <string name="phoneLabelsGroup">電話</string>
+  <string name="photoPickerNotFoundText">電話上無可用圖片。</string>
+  <string name="pickerNewContactHeader">建立新連絡人</string>
+  <string name="postalLabelsGroup">郵遞地址</string>
+  <string name="private_num">私人號碼</string>
+  <string name="recentCallsIconLabel">撥號記錄</string>
+  <string name="recentCalls_addToContact">新增到連絡人</string>
+  <string name="recentCalls_callNumber">撥打到 <xliff:g id="who">%s</xliff:g></string>
+  <string name="recentCalls_deleteAll">清除撥號記錄</string>
+  <string name="recentCalls_editNumberBeforeCall">撥號之前編輯號碼</string>
+  <string name="recentCalls_empty">撥號記錄空白。</string>
+  <string name="recentCalls_removeFromRecentList">從撥號記錄中移除</string>
+  <string name="removePicture">移除圖示</string>
+  <string name="searchHint">搜尋連絡人</string>
+  <string name="selectGroupsToSync">同步群組</string>
+  <string name="selectLabel">選取標籤</string>
+  <string name="select_group_title">選取要顯示的群組</string>
+  <string name="send_to_voicemail_checkbox">直接撥號到語音信箱。</string>
+  <string name="send_to_voicemail_view">如此便會直接撥號到語音信箱。</string>
+  <string name="shortcutContact">連絡人</string>
+  <string name="showAllGroups">所有連絡人</string>
+  <string name="showingGroupTitle">
+					<xliff:g id="groupName">%s</xliff:g>
+				</string>
+  <string name="simContacts_emptyLoading">正在從 SIM 卡載入\u2026</string>
+  <string name="simContacts_title">SIM 卡連絡人</string>
+  <string name="sms_entry_label">傳送簡訊/多媒體簡訊</string>
+  <string name="starredList">附加星號</string>
+  <string name="strequentList">我的最愛</string>
+  <string name="syncAllGroups">同步所有連絡人</string>
+  <string name="syncGroupChooserTitle">選取要同步的群組</string>
+  <string name="syncGroupPreference">同步群組</string>
+  <string name="syncGroupPreference_summary">選取要同步的群組</string>
+  <string name="unknown">無法辨識</string>
+  <string name="viewContactDesription">檢視連絡人</string>
+  <string name="viewContactTitle">連絡人詳細資料</string>
+  <string name="voicemail">語音信箱</string>
+  <string name="yesButton">確定</string>
+</resources>
diff --git a/res/values/ids.xml b/res/values/ids.xml
new file mode 100644
index 0000000..fb1fe41
--- /dev/null
+++ b/res/values/ids.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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>
+    <!-- The EditText for entries in the EditContactActivity -->
+    <item type="id" name="data" />
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..c09d213
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Strings for ContactsActivity and tabs -->
+    <string name="contactsList">Contacts</string>
+    <string name="shortcutContact">Contact</string>
+    <string name="starredList">Starred</string>
+    <string name="frequentList">Frequent</string>
+    <string name="strequentList">Favorites</string>
+
+    <!-- The title bar when viewing the contact details activity -->
+    <string name="viewContactTitle">Contact details</string>
+    
+    <!-- Individual contact context menu item -->
+    <string name="viewContactDesription">View contact</string>
+    
+    <!-- Individual contact context menu item. The description of the activity that edits a contact -->
+    <string name="editContactDescription">Edit contact</string>
+
+    <!-- The description of the activity that creates a contact -->
+    <string name="insertContactDescription">Create contact</string>
+    <!-- title of drop-down menu after user presses the Search key -->
+    <string name="searchHint">Search contacts</string>
+
+    <!-- Menu item to create a new contact -->
+    <string name="menu_newContact">New contact</string>
+    <!-- Menu item to create a new contact that is a "favorite" contact -->
+    <string name="menu_newFavortie">New favorite</string>
+    <string name="menu_dialer">Dialer</string>
+    <!-- Individual contact context menu item. -->
+    <string name="menu_viewContact">View contact</string>
+    <string name="menu_callNumber">Call <xliff:g id="contact">%s</xliff:g></string>
+    <string name="menu_editNumberBeforeCall">Edit number before call</string>
+    <!-- Individual contact context menu item. -->
+    <string name="menu_addStar">Add to favorites</string>
+    <!-- Individual contact context menu item. -->
+    <string name="menu_removeStar">Remove from favorites</string>
+
+    <!-- Strings for ViewContactActivity. -->
+    <string name="menu_editContact">Edit contact</string>
+    <!-- Individual contact context menu item & View contact screen menu item. -->
+    <string name="menu_deleteContact">Delete contact</string>
+    <string name="menu_call">Call</string>
+    <string name="menu_sendSMS">Send SMS/MMS</string>
+    <!-- Email address context menu item. -->
+    <string name="menu_sendEmail">Send email</string>
+    <!-- Physical address context menu item. -->
+    <string name="menu_viewAddress">Map address</string>
+    <!-- Phone number context menu item. -->
+    <string name="menu_makeDefaultNumber">Make default number</string>
+    <!-- Confirmation dialog after users selects to delete a contact. -->
+    <string name="deleteConfirmation_title">Delete</string>
+    <string name="deleteConfirmation">This contact will be deleted.</string>
+    <string name="yesButton">OK</string>
+    <string name="noButton">Cancel</string>
+
+    <!-- The label next to phone numbers in the contact details screen when clicking the number
+         will send an SMS to it. 
+    -->
+    <string name="sms_entry_label">Send SMS/MMS</string>
+
+    <!-- Strings for EditContact screen -->
+    <string name="menu_done">Save</string>
+    <string name="menu_doNotSave">Discard changes</string>
+
+    <string name="button_save">Save</string>
+    <string name="button_discard">Discard changes</string>
+
+    <string name="editContact_title_edit">Edit contact</string>
+    <string name="editContact_title_insert">New contact</string>
+
+    <string name="menu_addItem">More info</string>
+
+    <string name="label_company">Company</string>
+    <string name="label_title">Job title</string>
+    <string name="label_notes">Notes</string>
+    <string name="label_ringtone">Ringtone</string>
+    <!-- Ghost data that appears in the field by default in light font. -->
+    <string name="ghostData_name">First and Last</string>
+    <string name="ghostData_company">Company</string>
+    <string name="ghostData_title">Position</string>
+    <string name="ghostData_im">IM name</string>
+    <string name="ghostData_notes">My note</string>
+    <string name="ghostData_phone">Phone number</string>
+    <string name="ghostData_email">Email address</string>
+    <string name="ghostData_postal">Postal address</string>
+
+    <!-- Message displayed in a toast when you try to view the details of a contact that
+         for some reason doesn't exist anymore. -->
+    <string name="invalidContactMessage">The contact does not exist.</string>
+
+    <string name="pickerNewContactHeader">Create new contact</string>
+    <!-- New contact screen, dialog title when you select a label. -->
+    <string name="selectLabel">Select label</string>
+
+    <string name="phoneLabelsGroup">Phone</string>
+    <string name="emailLabelsGroup">Email</string>
+    <string name="imLabelsGroup">IM</string>
+    <string name="postalLabelsGroup">Postal address</string>
+    <string name="otherLabelsGroup">Other</string>
+
+    <string-array name="otherLabels">
+        <!-- The order of these is important, don't reorder without changing EditContactActivity.java -->
+        <item>Organization</item>
+        <item>Note</item>
+    </string-array>
+
+
+    <!-- Dialog that appears if there are no pictures from which to create an icon -->
+    <string name="errorDialogTitle">No pictures</string>
+    <string name="photoPickerNotFoundText">No pictures are available on the phone.</string>
+    <string name="okButtonText">OK</string>
+    <string name="cancelButtonText">Cancel</string>
+
+    <!-- Strings for AttachImage. -->
+    <string name="attachToContact">Contact icon</string>
+
+    <!-- unreviewed strings -->
+    <string name="customLabelPickerTitle">Custom label</string>
+    <!-- The menu item to open the preferences -->
+    <string name="menu_preferences">Settings</string>
+    <!-- The menu item to open the list of groups to display -->
+    <string name="menu_displayGroup">Display group</string>
+
+    <!-- The title of the preference to display which groups to sync -->
+    <string name="syncGroupPreference">Sync groups</string>
+    <!-- The summary of the preference to choose sync groups. -->
+    <string name="syncGroupPreference_summary">Select groups to synchronize</string>
+    
+    <!-- The description of the preference that launches the SIM import activity -->
+    <string name="importFromSim">SIM contacts importer</string>
+
+    <!-- Characters that are used for each of the fast-scroll widget that skips directly
+         to names starting with this letter.  If your language sorts differently than this,
+         please rearrange the alphabet here to be whatever is correct (e.g. Norwegian
+         has the three additional letters at the end). -->
+    <string name="alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
+    <!-- New contact screen, check box label. -->
+    <string name="send_to_voicemail_checkbox">Send calls directly to voicemail.</string>
+    <!-- View contact screen, string identifying state. -->
+    <string name="send_to_voicemail_view">Calls are sent directly to voicemail.</string>
+    <!-- New contact screen, default ringtone value. -->
+    <string name="default_ringtone">Default</string>
+
+    <!-- The button/menu item that allows you to add a picture to a contact -->
+    <string name="addPicture">Add icon</string>
+
+    <!-- The menu item that allows you to remove a picture from a contact -->
+    <string name="removePicture">Remove icon</string>
+
+    <!-- The text displayed when the contacts list is empty while displaying all contacts -->
+    <string name="noContacts">No contacts.</string>
+
+    <!-- The text displayed when the contacts list is empty while displaying only contacts that have phone numbers -->
+    <string name="noContactsWithPhoneNumbers">No contacts with phone numbers.</string>
+
+    <!-- The text displayed when the contacts list is empty while displaying favorite contacts -->
+    <string name="noFavorites">No favorites.</string>
+
+    <!-- Title for group selection menu -->
+    <string name="select_group_title">Select group to display</string>
+    
+    <!-- Group name for all contacts -->
+    <string name="allContacts">All contacts</string>
+    
+    <!-- Title for Contacts group that is currently showing -->
+    <string name="showingGroupTitle"><xliff:g id="groupName">%s</xliff:g></string>
+    
+    <!-- The text displayed when the contacts list is empty while displaying a single group -->
+    <string name="groupEmpty">Your \"<xliff:g id="groupName">%s</xliff:g>\" group is empty.</string>
+
+    <!-- The description of all groups when asking the user what they want to display -->
+    <string name="showAllGroups">All contacts</string>
+
+    <!-- The setting to sync all contacts from the server -->
+    <string name="syncAllGroups">Sync all contacts</string>
+
+    <!-- The name of the system "My Contacts" group. This should be kept in sync with the web UI -->
+    <string name="groupNameMyContacts">My Contacts</string>
+    
+    <!-- The group type that displays only contacts with phone numbers -->
+    <string name="groupNameWithPhones">Contacts with phone numbers</string>
+    
+    <!-- Title of the dialog the user uses to choose which contacts groups to sync from the server -->
+    <string name="syncGroupChooserTitle">Select groups to sync</string>
+
+    <!-- The menu item allowing you to select which groups you want to sync. -->
+    <string name="selectGroupsToSync">Sync groups</string>
+
+    <!-- Toast displayed when a contact is created -->
+    <string name="contactCreatedToast">Contact created.</string>
+    
+    <!-- Toast displayed when a contact is saved -->
+    <string name="contactSavedToast">Contact saved.</string>
+
+    <!-- Separator in the contact details list describing that the items below it will place a call when clicked -->
+    <string name="listSeparatorCallNumber">Dial number</string>
+    
+    <!-- Separator in the contact details list describing that the items below it will send an SMS/MMS to a phone number -->
+    <string name="listSeparatorSendSmsMms">Send SMS/MMS</string>
+
+    <!-- Separator in the contact details list describing that the items below it will send an email -->
+    <string name="listSeparatorSendEmail">Send email</string>
+
+    <!-- Separator in the contact details list describing that the items below it will send an IM -->
+    <string name="listSeparatorSendIm">Send instant message</string>
+
+    <!-- Separator in the contact details list describing that the items below it will open maps with the given address -->
+    <string name="listSeparatorMapAddress">Map address</string>
+
+    <!-- Separator in the contact details list describing that the items below are non-actionable organization information -->
+    <string name="listSeparatorOrganizations">Organizations</string>
+
+    <!-- Separator in the contact details list describing that the items below are random other non-actionable information about a contact -->
+    <string name="listSeparatorOtherInformation">Other information</string>
+
+
+
+    <!-- Launcher tab labels -->
+    <string name="contactsIconLabel">Contacts</string>
+    <string name="contactsFavoritesLabel">Favorites</string>
+    <string name="dialerIconLabel">Dialer</string>
+    <string name="phoneIconLabel">Phone</string>
+    <string name="recentCallsIconLabel">Call log</string>
+
+    <!-- menu items for Dialer  -->
+    <string name="menu_sendTextMessage">Send SMS message</string>
+
+    <!-- Recent calls strings -->
+    <string name="recentCalls_callNumber">Call <xliff:g id="who">%s</xliff:g></string>
+    <string name="recentCalls_editNumberBeforeCall">Edit number before call</string>
+    <string name="recentCalls_addToContact">Add to contacts</string>
+    <string name="recentCalls_removeFromRecentList">Remove from call log</string>
+    <string name="recentCalls_deleteAll">Clear call log</string>
+    <string name="recentCalls_empty">Call log is empty.</string>
+
+    <!-- IMEI strings -->
+    <string name="imei">IMEI</string>
+    <string name="no_imei">Unknown IMEI</string>
+
+    <string name="doneButton">Done</string>
+
+    <string name="ok">OK</string>
+
+    <string name="voicemail">Voicemail</string>
+    <string name="unknown">Unknown</string>
+    <string name="private_num">Private number</string>
+
+    <!-- Displayed in the text entry box in the dialer when in landscape mode to guide the user
+         to dial using the physical keyboard -->
+    <string name="dialerKeyboardHintText">Use keyboard to dial</string>
+
+    <!-- ADN related strings -->
+    <string name="simContacts_emptyLoading">Loading from SIM card\u2026</string>
+    <string name="simContacts_title">SIM card contacts</string>
+</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 0000000..0648441
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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>
+    <style name="TextAppearance.DialerLine1" parent="@android:style/TextAppearance.Widget.Button">
+        <item name="android:textSize">32sp</item>
+        <item name="android:textColor">@color/dialer_button_text</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+
+    <style name="TextAppearance.DialerLine2" parent="@android:style/TextAppearance.Widget.Button">
+        <item name="android:textSize">18sp</item>
+        <item name="android:textColor">@color/dialer_button_text</item>
+    </style>
+</resources>
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
new file mode 100644
index 0000000..dd18137
--- /dev/null
+++ b/res/xml/preferences.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <PreferenceScreen
+            android:key="sync_groups"
+            android:title="@string/syncGroupPreference"
+            android:summary="@string/syncGroupPreference_summary">
+        <intent
+                android:action="android.intent.action.VIEW"
+                android:targetPackage="com.android.contacts"
+                android:targetClass="com.android.contacts.ContactsGroupSyncSelector"
+        />
+    </PreferenceScreen>
+
+    <PreferenceScreen
+        android:title="@string/importFromSim"
+    >
+
+        <intent
+            android:action="android.intent.action.VIEW"
+            android:mimeType="vnd.android.cursor.item/sim-contact"
+            android:targetPackage="com.android.phone"
+            android:targetClass="com.android.phone.SimContacts"
+        />
+    </PreferenceScreen>
+</PreferenceScreen>
diff --git a/res/xml/searchable.xml b/res/xml/searchable.xml
new file mode 100644
index 0000000..cd9e6d6
--- /dev/null
+++ b/res/xml/searchable.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<!--
+    TODO: Use localizable string resources instead of hard-coded strings
+    once Search Manager is enhanced to allow them.
+-->
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+    android:icon="@drawable/ic_tab_contacts"
+    android:label="@string/contactsList"
+    android:hint="@string/searchHint"
+    android:searchMode="queryRewriteFromText"
+
+    android:searchSuggestAuthority="contacts"
+    android:searchSuggestIntentAction="android.provider.Contacts.SEARCH_SUGGESTION_CLICKED"
+    android:searchSuggestIntentData="content://contacts/people"
+>
+
+    <!-- allow green action key for search-bar and per-suggestion clicks -->
+    <actionkey
+        android:keycode="KEYCODE_CALL"
+        android:queryActionMsg="call"
+        android:suggestActionMsg="call"
+    />
+
+</searchable>
diff --git a/src/com/android/contacts/AlphabetIndexer.java b/src/com/android/contacts/AlphabetIndexer.java
new file mode 100644
index 0000000..4e5fb0f
--- /dev/null
+++ b/src/com/android/contacts/AlphabetIndexer.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2008 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.android.contacts;
+
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.util.SparseIntArray;
+
+/**
+ * This class essentially helps in building an index of section boundaries of a
+ * sorted column of a cursor. For instance, if a cursor contains a data set 
+ * sorted by first name of a person or the title of a song, this class will 
+ * perform a binary search to identify the first row that begins with a 
+ * particular letter. The search is case-insensitive. The class caches the index 
+ * such that subsequent queries for the same letter will return right away.
+ */
+public class AlphabetIndexer extends DataSetObserver {
+
+    protected Cursor mDataCursor;
+    protected int mColumnIndex;
+    protected Object[] mAlphabetArray;
+    private SparseIntArray mAlphaMap;
+    private java.text.Collator mCollator;
+
+    /**
+     * Constructs the indexer.
+     * @param cursor the cursor containing the data set
+     * @param columnIndex the column number in the cursor that is sorted 
+     *        alphabetically
+     * @param sections the array of objects that represent the sections. The
+     * toString() method of each item is called and the first letter of the 
+     * String is used as the letter to search for.
+     */
+    public AlphabetIndexer(Cursor cursor, int columnIndex, Object[] sections) {
+        mDataCursor = cursor;
+        mColumnIndex = columnIndex;
+        mAlphabetArray = sections;
+        mAlphaMap = new SparseIntArray(26 /* Optimize for English */);
+        if (cursor != null) {
+            cursor.registerDataSetObserver(this);
+        }
+        // Get a Collator for the current locale for string comparisons.
+        mCollator = java.text.Collator.getInstance();
+        mCollator.setStrength(java.text.Collator.PRIMARY);
+    }
+
+    /**
+     * Sets a new cursor as the data set and resets the cache of indices.
+     * @param cursor the new cursor to use as the data set
+     */
+    public void setCursor(Cursor cursor) {
+        if (mDataCursor != null) {
+            mDataCursor.unregisterDataSetObserver(this);
+        }
+        mDataCursor = cursor;
+        if (cursor != null) {
+            mDataCursor.registerDataSetObserver(this);
+        }
+        mAlphaMap.clear();
+    }
+
+    /**
+     * Performs a binary search or cache lookup to find the first row that
+     * matches a given section's starting letter.
+     * @param sectionIndex the section to search for
+     * @return the row index of the first occurrence, or the nearest next letter.
+     * For instance, if searching for "T" and no "T" is found, then the first
+     * row starting with "U" or any higher letter is returned. If there is no
+     * data following "T" at all, then the list size is returned.
+     */
+    public int indexOf(int sectionIndex) {
+        final SparseIntArray alphaMap = mAlphaMap;
+        final Cursor cursor = mDataCursor;
+
+        if (cursor == null || mAlphabetArray == null) {
+            return 0;
+        }
+        
+        // Check bounds
+        if (sectionIndex <= 0) {
+            return 0;
+        }
+        if (sectionIndex >= mAlphabetArray.length) {
+            sectionIndex = mAlphabetArray.length - 1;
+        }
+
+        int savedCursorPos = cursor.getPosition();
+
+        int count = cursor.getCount();
+        int start = 0;
+        int end = count;
+        int pos;
+
+        String letter = mAlphabetArray[sectionIndex].toString();
+        letter = letter.toUpperCase();
+        int key = letter.charAt(0);
+        // Check map
+        if (Integer.MIN_VALUE != (pos = alphaMap.get(key, Integer.MIN_VALUE))) {
+            // Is it approximate? Using negative value to indicate that it's 
+            // an approximation and positive value when it is the accurate
+            // position.
+            if (pos < 0) {
+                pos = -pos;
+                end = pos;
+            } else {
+                // Not approximate, this is the confirmed start of section, return it
+                return pos;
+            }
+        }
+
+        // Do we have the position of the previous section?
+        if (sectionIndex > 0) {
+            int prevLetter =
+                    mAlphabetArray[sectionIndex - 1].toString().charAt(0);
+            int prevLetterPos = alphaMap.get(prevLetter, Integer.MIN_VALUE);
+            if (prevLetterPos != Integer.MIN_VALUE) {
+                start = Math.abs(prevLetterPos);
+            }
+        }
+
+        // Now that we have a possibly optimized start and end, let's binary search
+
+        pos = (end + start) / 2;
+
+        while (pos < end) {
+            // Get letter at pos
+            cursor.moveToPosition(pos);
+            String curName = cursor.getString(mColumnIndex);
+            if (curName == null) {
+                if (pos == 0) {
+                    break;
+                } else {
+                    pos--;
+                    continue;
+                }
+            }
+            int curLetter = Character.toUpperCase(curName.charAt(0));
+
+            if (curLetter != key) {
+                // Enter approximation in hash if a better solution doesn't exist
+                int curPos = alphaMap.get(curLetter, Integer.MIN_VALUE);
+                if (curPos == Integer.MIN_VALUE || Math.abs(curPos) > pos) {
+                    // Negative pos indicates that it is an approximation
+                    alphaMap.put(curLetter, -pos);
+                }
+                if (mCollator.compare(curName, letter) < 0) {
+                    start = pos + 1;
+                    if (start >= count) {
+                        pos = count;
+                        break;
+                    }
+                } else {
+                    end = pos;
+                }
+            } else {
+                // They're the same, but that doesn't mean it's the start
+                if (start == pos) {
+                    // This is it
+                    break;
+                } else {
+                    // Need to go further lower to find the starting row
+                    end = pos;
+                }
+            }
+            pos = (start + end) / 2;
+        }
+        alphaMap.put(key, pos);
+        cursor.moveToPosition(savedCursorPos);
+        return pos;
+    }
+
+    @Override
+    public void onChanged() {
+        super.onChanged();
+        mAlphaMap.clear();
+    }
+
+    @Override
+    public void onInvalidated() {
+        super.onInvalidated();
+        mAlphaMap.clear();
+    }
+}
diff --git a/src/com/android/contacts/AttachImage.java b/src/com/android/contacts/AttachImage.java
new file mode 100644
index 0000000..6346031
--- /dev/null
+++ b/src/com/android/contacts/AttachImage.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2006 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.android.contacts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Contacts;
+import android.provider.Contacts.People;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Provides an external interface for other applications to attach images
+ * to contacts. It will first present a contact picker and then run the 
+ * image that is handed to it through the cropper to make the image the proper
+ * size and give the user a chance to use the face detector.
+ */
+class AttachImage extends Activity {
+    private static final int REQUEST_PICK_CONTACT = 1;
+    private static final int REQUEST_CROP_PHOTO = 2;
+
+    private static final String CONTACT_URI_KEY = "contact_uri";
+
+    public AttachImage() {
+        
+    }
+
+    Uri mContactUri;
+    
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        if (icicle != null) {
+            mContactUri = icicle.getParcelable(CONTACT_URI_KEY);
+        } else {
+            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+            intent.setType(People.CONTENT_ITEM_TYPE);
+            startActivityForResult(intent, REQUEST_PICK_CONTACT);
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        if (mContactUri != null) {
+            outState.putParcelable(CONTACT_URI_KEY, mContactUri);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent result) {
+        if (resultCode != RESULT_OK) {
+            finish();
+            return;
+        }
+
+        if (requestCode == REQUEST_PICK_CONTACT) {
+            mContactUri = result.getData();
+            // A contact was picked. Launch the cropper to get face detection, the right size, etc.
+            // TODO: get these values from constants somewhere
+            Intent myIntent = getIntent();
+            Intent intent = new Intent("com.android.camera.action.CROP", myIntent.getData());
+            if (myIntent.getStringExtra("mimeType") != null) {
+                intent.setDataAndType(myIntent.getData(), myIntent.getStringExtra("mimeType"));
+            }
+            intent.putExtra("crop", "true");
+            intent.putExtra("aspectX", 1);
+            intent.putExtra("aspectY", 1);
+            intent.putExtra("outputX", 96);
+            intent.putExtra("outputY", 96);
+            intent.putExtra("return-data", true);
+            startActivityForResult(intent, REQUEST_CROP_PHOTO);
+        } else if (requestCode == REQUEST_CROP_PHOTO) {
+            final Bundle extras = result.getExtras();
+            if (extras != null) {
+                Bitmap photo = extras.getParcelable("data");
+                if (photo != null) {
+                    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+                    photo.compress(Bitmap.CompressFormat.JPEG, 75, stream);
+                    Contacts.People.setPhotoData(getContentResolver(), mContactUri,
+                            stream.toByteArray());
+                }
+            }
+            finish();
+        }
+    }
+}
diff --git a/src/com/android/contacts/ButtonGridLayout.java b/src/com/android/contacts/ButtonGridLayout.java
new file mode 100644
index 0000000..e3431b1
--- /dev/null
+++ b/src/com/android/contacts/ButtonGridLayout.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 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.android.contacts;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.MeasureSpec;
+
+public class ButtonGridLayout extends ViewGroup {
+
+    private final int mColumns = 3;
+    
+    public ButtonGridLayout(Context context) {
+        super(context);
+    }
+
+    public ButtonGridLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ButtonGridLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+    
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int y = mPaddingTop;
+        final int rows = getRows();
+        final View child0 = getChildAt(0);
+        final int yInc = (getHeight() - mPaddingTop - mPaddingBottom) / rows;
+        final int xInc = (getWidth() - mPaddingLeft - mPaddingRight) / mColumns;
+        final int childWidth = child0.getMeasuredWidth();
+        final int childHeight = child0.getMeasuredHeight();
+        final int xOffset = (xInc - childWidth) / 2;
+        final int yOffset = (yInc - childHeight) / 2;
+        
+        for (int row = 0; row < rows; row++) {
+            int x = mPaddingLeft;
+            for (int col = 0; col < mColumns; col++) {
+                int cell = row * mColumns + col;
+                if (cell >= getChildCount()) {
+                    break;
+                }
+                View child = getChildAt(cell);
+                child.layout(x + xOffset, y + yOffset, 
+                        x + xOffset + childWidth, 
+                        y + yOffset + childHeight);
+                x += xInc;
+            }
+            y += yInc;
+        }
+    }
+
+    private int getRows() {
+        return (getChildCount() + mColumns - 1) / mColumns; 
+    }
+    
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int width = mPaddingLeft + mPaddingRight;
+        int height = mPaddingTop + mPaddingBottom;
+        
+        // Measure the first child and get it's size
+        View child = getChildAt(0);
+        child.measure(MeasureSpec.UNSPECIFIED , MeasureSpec.UNSPECIFIED);
+        int childWidth = child.getMeasuredWidth();
+        int childHeight = child.getMeasuredHeight();
+        // Make sure the other children are measured as well, to initialize
+        for (int i = 1; i < getChildCount(); i++) {
+            getChildAt(0).measure(MeasureSpec.UNSPECIFIED , MeasureSpec.UNSPECIFIED);
+        }
+        // All cells are going to be the size of the first child
+        width += mColumns * childWidth;
+        height += getRows() * childHeight;
+        
+        width = resolveSize(width, widthMeasureSpec);
+        height = resolveSize(height, heightMeasureSpec);
+        setMeasuredDimension(width, height);
+    }
+
+}
diff --git a/src/com/android/contacts/ContactEntryAdapter.java b/src/com/android/contacts/ContactEntryAdapter.java
new file mode 100644
index 0000000..981b4bc
--- /dev/null
+++ b/src/com/android/contacts/ContactEntryAdapter.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2007 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.android.contacts;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Parcel;
+import android.provider.Contacts.Organizations;
+import android.provider.Contacts.People;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import java.util.ArrayList;
+
+public abstract class ContactEntryAdapter<E extends ContactEntryAdapter.Entry>
+        extends BaseAdapter {
+
+    public static final String[] CONTACT_PROJECTION = new String[] {
+        People._ID, // 0
+        People.NAME, // 1
+        People.NOTES, // 2
+        People.PRIMARY_PHONE_ID, // 3
+        People.PRESENCE_STATUS, // 4
+        People.STARRED, // 5
+        People.CUSTOM_RINGTONE, // 6
+        People.SEND_TO_VOICEMAIL, // 7
+    };
+    public static final int CONTACT_ID_COLUMN = 0;
+    public static final int CONTACT_NAME_COLUMN = 1;
+    public static final int CONTACT_NOTES_COLUMN = 2;
+    public static final int CONTACT_PREFERRED_PHONE_COLUMN = 3;
+    public static final int CONTACT_SERVER_STATUS_COLUMN = 4;
+    public static final int CONTACT_STARRED_COLUMN = 5;
+    public static final int CONTACT_CUSTOM_RINGTONE_COLUMN = 6;
+    public static final int CONTACT_SEND_TO_VOICEMAIL_COLUMN = 7;
+
+    public static final String[] PHONES_PROJECTION = new String[] {
+        People.Phones._ID, // 0
+        People.Phones.NUMBER, // 1
+        People.Phones.TYPE, // 2
+        People.Phones.LABEL, // 3
+        People.Phones.ISPRIMARY, // 4
+    };
+    public static final int PHONES_ID_COLUMN = 0;
+    public static final int PHONES_NUMBER_COLUMN = 1;
+    public static final int PHONES_TYPE_COLUMN = 2;
+    public static final int PHONES_LABEL_COLUMN = 3;
+    public static final int PHONES_ISPRIMARY_COLUMN = 4;
+
+    public static final String[] METHODS_PROJECTION = new String[] {
+        People.ContactMethods._ID, // 0
+        People.ContactMethods.KIND, // 1
+        People.ContactMethods.DATA, // 2
+        People.ContactMethods.TYPE, // 3
+        People.ContactMethods.LABEL, // 4
+        People.ContactMethods.ISPRIMARY, // 5
+        People.ContactMethods.AUX_DATA, // 6
+    };
+    public static final String[] METHODS_WITH_PRESENCE_PROJECTION = new String[] {
+        People.ContactMethods._ID, // 0
+        People.ContactMethods.KIND, // 1
+        People.ContactMethods.DATA, // 2
+        People.ContactMethods.TYPE, // 3
+        People.ContactMethods.LABEL, // 4
+        People.ContactMethods.ISPRIMARY, // 5
+        People.ContactMethods.AUX_DATA, // 6
+        People.PRESENCE_STATUS, // 7
+    };
+    public static final int METHODS_ID_COLUMN = 0;
+    public static final int METHODS_KIND_COLUMN = 1;
+    public static final int METHODS_DATA_COLUMN = 2;
+    public static final int METHODS_TYPE_COLUMN = 3;
+    public static final int METHODS_LABEL_COLUMN = 4;
+    public static final int METHODS_ISPRIMARY_COLUMN = 5;
+    public static final int METHODS_AUX_DATA_COLUMN = 6;
+    public static final int METHODS_STATUS_COLUMN = 7;
+
+    public static final String[] ORGANIZATIONS_PROJECTION = new String[] {
+        Organizations._ID, // 0
+        Organizations.TYPE, // 1
+        Organizations.LABEL, // 2
+        Organizations.COMPANY, // 3
+        Organizations.TITLE, // 4
+        Organizations.ISPRIMARY, // 5
+    };
+    public static final int ORGANIZATIONS_ID_COLUMN = 0;
+    public static final int ORGANIZATIONS_TYPE_COLUMN = 1;
+    public static final int ORGANIZATIONS_LABEL_COLUMN = 2;
+    public static final int ORGANIZATIONS_COMPANY_COLUMN = 3;
+    public static final int ORGANIZATIONS_TITLE_COLUMN = 4;
+    public static final int ORGANIZATIONS_ISPRIMARY_COLUMN = 5;
+    
+    protected ArrayList<ArrayList<E>> mSections;
+    protected LayoutInflater mInflater;
+    protected Context mContext;
+    protected boolean mSeparators;
+
+    /**
+     * Base class for adapter entries.
+     */
+    public static class Entry {
+        /** Details from the person table */
+        public static final int KIND_CONTACT = -1;
+        /** Synthesized phone entry that will send an SMS instead of call the number */
+        public static final int KIND_SMS = -2;
+        /** A section separator */
+        public static final int KIND_SEPARATOR = -3; 
+
+        public String label;
+        public String data;
+        public Uri uri;
+        public long id = 0;
+        public int maxLines = 1;
+        public int kind;
+        
+        /**
+         * Helper for making subclasses parcelable.
+         */
+        protected void writeToParcel(Parcel p) {
+            p.writeString(label);
+            p.writeString(data);
+            p.writeParcelable(uri, 0);
+            p.writeLong(id);
+            p.writeInt(maxLines);
+            p.writeInt(kind);
+        }
+        
+        /**
+         * Helper for making subclasses parcelable.
+         */
+        protected void readFromParcel(Parcel p) {
+            label = p.readString();
+            data = p.readString();
+            uri = p.readParcelable(null);
+            id = p.readLong();
+            maxLines = p.readInt();
+            kind = p.readInt();
+        }
+    }
+
+    ContactEntryAdapter(Context context, ArrayList<ArrayList<E>> sections, boolean separators) {
+        mContext = context;
+        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        mSections = sections;
+        mSeparators = separators;
+    }
+
+    /**
+     * Resets the section data.
+     * 
+     * @param sections the section data
+     */
+    public final void setSections(ArrayList<ArrayList<E>> sections, boolean separators) {
+        mSections = sections;
+        mSeparators = separators;
+        notifyDataSetChanged();
+    }
+
+    /**
+     * Resets the section data and returns the position of the given entry.
+     * 
+     * @param sections the section data
+     * @param entry the entry to return the position for
+     * @return the position of entry, or -1 if it isn't found
+     */
+    public final int setSections(ArrayList<ArrayList<E>> sections, E entry) {
+        mSections = sections;
+        notifyDataSetChanged();
+
+        int numSections = mSections.size();
+        int position = 0;
+        for (int i = 0; i < numSections; i++) {
+            ArrayList<E> section = mSections.get(i);
+            int sectionSize = section.size();
+            for (int j = 0; j < sectionSize; j++) {
+                E e = section.get(j);
+                if (e.equals(entry)) {
+                    position += j;
+                    return position;
+                }
+            }
+            position += sectionSize;
+        }
+        return -1;
+    }
+
+    /**
+     * @see android.widget.ListAdapter#getCount()
+     */
+    public final int getCount() {
+        return countEntries(mSections, mSeparators);
+    }
+
+    /**
+     * @see android.widget.ListAdapter#hasSeparators()
+     */
+    @Override
+    public final boolean areAllItemsEnabled() {
+        return mSeparators == false;
+    }
+
+    /**
+     * @see android.widget.ListAdapter#isSeparator(int)
+     */
+    @Override
+    public final boolean isEnabled(int position) {
+        if (!mSeparators) {
+            return true;
+        }
+
+        int numSections = mSections.size();
+        for (int i = 0; i < numSections; i++) {
+            ArrayList<E> section = mSections.get(i);
+            int sectionSize = section.size();
+            if (sectionSize == 1) {
+                // The section only contains a separator and nothing else, skip it
+                continue;
+            }
+            if (position == 0) {
+                // The first item in a section is always the separator
+                return false;
+            }
+            position -= sectionSize;
+        }
+        return true;
+    }
+
+    /**
+     * @see android.widget.ListAdapter#getItem(int)
+     */
+    public final Object getItem(int position) {
+        return getEntry(mSections, position, mSeparators);
+    }
+
+    /**
+     * Get the entry for the given position.
+     * 
+     * @param sections the list of sections
+     * @param position the position for the desired entry
+     * @return the ContactEntry for the given position
+     */
+    public final static <T extends Entry> T getEntry(ArrayList<ArrayList<T>> sections,
+            int position, boolean separators) {
+        int numSections = sections.size();
+        for (int i = 0; i < numSections; i++) {
+            ArrayList<T> section = sections.get(i);
+            int sectionSize = section.size();
+            if (separators && sectionSize == 1) {
+                // The section only contains a separator and nothing else, skip it
+                continue;
+            }
+            if (position < section.size()) {
+                return section.get(position);
+            }
+            position -= section.size();
+        }
+        return null;
+    }
+
+    /**
+     * Get the count of entries in all sections
+     * 
+     * @param sections the list of sections
+     * @return the count of entries in all sections
+     */
+    public static <T extends Entry> int countEntries(ArrayList<ArrayList<T>> sections,
+            boolean separators) {
+        int count = 0;
+        int numSections = sections.size();
+        for (int i = 0; i < numSections; i++) {
+            ArrayList<T> section = sections.get(i);
+            int sectionSize = section.size();
+            if (separators && sectionSize == 1) {
+                // The section only contains a separator and nothing else, skip it
+                continue;
+            }
+            count += sections.get(i).size();
+        }
+        return count;
+    }
+
+    /**
+     * @see android.widget.ListAdapter#getItemId(int)
+     */
+    public final long getItemId(int position) {
+        Entry entry = getEntry(mSections, position, mSeparators);
+        if (entry != null) {
+            return entry.id;
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
+     */
+    public View getView(int position, View convertView, ViewGroup parent) {
+        View v;
+        if (convertView == null) {
+            v = newView(position, parent);
+        } else {
+            v = convertView;
+        }
+        bindView(v, getEntry(mSections, position, mSeparators));
+        return v;
+    }
+
+    /**
+     * Create a new view for an entry.
+     * 
+     * @parent the parent ViewGroup
+     * @return the newly created view
+     */
+    protected abstract View newView(int position, ViewGroup parent);
+
+    /**
+     * Binds the data from an entry to a view.
+     * 
+     * @param view the view to display the entry in
+     * @param entry the data to bind
+     */
+    protected abstract void bindView(View view, E entry);
+}
diff --git a/src/com/android/contacts/ContactsGroupSyncSelector.java b/src/com/android/contacts/ContactsGroupSyncSelector.java
new file mode 100644
index 0000000..1043299
--- /dev/null
+++ b/src/com/android/contacts/ContactsGroupSyncSelector.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2008 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.android.contacts;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.Contacts;
+import android.provider.Contacts.Groups;
+import android.provider.Contacts.Settings;
+import android.text.TextUtils;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+public final class ContactsGroupSyncSelector extends AlertActivity implements 
+        ListView.OnItemClickListener, DialogInterface.OnClickListener {
+
+    private static final String[] PROJECTION = new String[] {
+            Groups._ID, // 0
+            Groups.NAME, // 1
+            Groups.SHOULD_SYNC, // 2
+            Groups.SYSTEM_ID, // 3
+    };
+    private static final int COLUMN_INDEX_ID = 0;
+    private static final int COLUMN_INDEX_NAME = 1;
+    private static final int COLUMN_INDEX_SHOULD_SYNC = 2;
+    private static final int COLUMN_INDEX_SYSTEM_ID = 3;
+
+    private ContentResolver mResolver;
+    private ListView mListView;
+    private GroupsAdapter mAdapter;
+
+    boolean[] mChecked;
+    boolean mSyncAllGroups;
+    long[] mGroupIds;
+    
+    private final class GroupsAdapter extends ArrayAdapter<CharSequence> {
+        public GroupsAdapter(CharSequence[] items) {
+            super(new ContextThemeWrapper(ContactsGroupSyncSelector.this,
+                    android.R.style.Theme_Light),
+                    android.R.layout.simple_list_item_checked,
+                    android.R.id.text1, items);
+        }
+
+        @Override
+        public boolean areAllItemsEnabled() {
+            return mSyncAllGroups; 
+        }
+
+        @Override
+        public boolean isEnabled(int pos) {
+            if (mSyncAllGroups && pos != 0) {
+                return false;
+            } else {
+                return true;
+            }
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View v = super.getView(position, convertView, parent);
+            if (mSyncAllGroups && position != 0) {
+                v.setEnabled(false);
+            } else {
+                v.setEnabled(true);
+            }
+            return v;
+        }
+    }
+
+    /**
+     * Handles clicks on the list items
+     */
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        boolean isChecked = mListView.isItemChecked(position);
+        mChecked[position] = isChecked;
+        if (position == 0) {
+            mSyncAllGroups = isChecked;
+            adjustChecks();
+        }
+    }
+
+    /**
+     * Handles clicks on the OK button
+     */
+    public void onClick(DialogInterface dialog, int which) {
+        if (mSyncAllGroups) {
+            // For now we only support a single account and the UI doesn't know what
+            // the account name is, so we're using a global setting for SYNC_EVERYTHING.
+            // Some day when we add multiple accounts to the UI this should use the per
+            // account setting.
+            Settings.setSetting(mResolver, null, Settings.SYNC_EVERYTHING, "1");
+        } else {
+            final ContentResolver resolver = mResolver;
+            ContentValues values = new ContentValues();
+            int count = mChecked.length;
+            for (int i = 1; i < count; i++) {
+                values.clear();
+                values.put(Groups.SHOULD_SYNC, mChecked[i]);
+                resolver.update(ContentUris.withAppendedId(Groups.CONTENT_URI, mGroupIds[i]),
+                        values, null, null);
+            }
+            // For now we only support a single account and the UI doesn't know what
+            // the account name is, so we're using a global setting for SYNC_EVERYTHING.
+            // Some day when we add multiple accounts to the UI this should use the per
+            // account setting.
+            Settings.setSetting(resolver, null, Settings.SYNC_EVERYTHING, "0");
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedState) {
+        super.onCreate(savedState);
+        mResolver = getContentResolver();
+
+        // Set the alert parameters
+        AlertController.AlertParams params = mAlertParams;
+        params.mTitle = getText(R.string.syncGroupChooserTitle);
+        params.mIcon = getResources().getDrawable(R.drawable.ic_tab_unselected_contacts);
+        params.mPositiveButtonText = getText(R.string.okButtonText);
+        params.mPositiveButtonListener = this;
+        params.mNegativeButtonText = getText(R.string.cancelButtonText);
+        buildItems(params);
+
+        // Takes the info in mAlertParams and creates the layout
+        setupAlert();
+
+        mListView = mAlert.getListView();
+        mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+        mListView.setOnItemClickListener(this);
+        adjustChecks();
+    }
+
+    private void buildItems(AlertController.AlertParams params) {
+        Cursor cursor = mResolver.query(Groups.CONTENT_URI, PROJECTION, null, null, Groups.NAME);
+        if (cursor != null) {
+            try {
+                int count = cursor.getCount() + 1;
+                CharSequence[] items = new String[count];
+                boolean[] checked = new boolean[count];
+                long[] groupIds = new long[count];
+    
+                int i = 0;
+                items[i++] = getString(R.string.syncAllGroups);
+                items[i++] = getString(R.string.groupNameMyContacts);
+    
+                while (cursor.moveToNext()) {
+                    String name = cursor.getString(COLUMN_INDEX_NAME);
+                    String systemId = cursor.isNull(COLUMN_INDEX_SYSTEM_ID) ?
+                            null : cursor.getString(COLUMN_INDEX_SYSTEM_ID);
+                    if (systemId == null || !Groups.GROUP_MY_CONTACTS.equals(systemId)) {
+                        items[i] = name;
+                        checked[i] = cursor.getInt(COLUMN_INDEX_SHOULD_SYNC) != 0;
+                        groupIds[i] = cursor.getLong(COLUMN_INDEX_ID);
+                        i++;
+                    } else {
+                        checked[1] = cursor.getInt(COLUMN_INDEX_SHOULD_SYNC) != 0;
+                        groupIds[1] = cursor.getLong(COLUMN_INDEX_ID);
+                    }
+                }
+                mChecked = checked;
+                mSyncAllGroups = getShouldSyncEverything(mResolver);
+                checked[0] = mSyncAllGroups;
+                mGroupIds = groupIds;
+    
+                // Setup the adapter
+                mAdapter = new GroupsAdapter(items);
+                params.mAdapter = mAdapter;
+            } finally {
+                cursor.close();
+            }
+        }
+    }
+
+    private void adjustChecks() {
+        ListView list = mListView;
+        if (mSyncAllGroups) {
+            int count = list.getCount();
+            for (int i = 0; i < count; i++) {
+                list.setItemChecked(i, true);
+            }
+        } else {
+            boolean[] checked = mChecked;
+            int count = list.getCount();
+            for (int i = 0; i < count; i++) {
+                list.setItemChecked(i, checked[i]);
+            }
+        }
+    }
+
+    private static boolean getShouldSyncEverything(ContentResolver cr) {
+        // For now we only support a single account and the UI doesn't know what
+        // the account name is, so we're using a global setting for SYNC_EVERYTHING.
+        // Some day when we add multiple accounts to the UI this should use the per
+        // account setting.
+        String value = Contacts.Settings.getSetting(cr, null, Contacts.Settings.SYNC_EVERYTHING);
+        if (value == null) {
+            // If nothing is set yet we default to syncing everything
+            return true;
+        }
+        return !TextUtils.isEmpty(value) && !"0".equals(value);
+    }
+}
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
new file mode 100644
index 0000000..0ffa786
--- /dev/null
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -0,0 +1,1407 @@
+/*
+ * Copyright (C) 2007 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.android.contacts;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.app.SearchManager;
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.CharArrayBuffer;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.preference.PreferenceManager;
+import android.provider.Contacts;
+import android.provider.Contacts.ContactMethods;
+import android.provider.Contacts.Groups;
+import android.provider.Contacts.Intents;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.Contacts.Presence;
+import android.provider.Contacts.Intents.UI;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.ResourceCursorAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * Displays a list of contacts. Usually is embedded into the ContactsActivity.
+ */
+public final class ContactsListActivity extends ListActivity
+        implements View.OnCreateContextMenuListener, DialogInterface.OnClickListener {
+    private static final String TAG = "ContactsListActivity";
+
+    private static final String LIST_STATE_KEY = "liststate";
+    private static final String FOCUS_KEY = "focused";
+
+    static final int MENU_ITEM_VIEW_CONTACT = 1;
+    static final int MENU_ITEM_CALL = 2;
+    static final int MENU_ITEM_EDIT_BEFORE_CALL = 3;
+    static final int MENU_ITEM_SEND_SMS = 4;
+    static final int MENU_ITEM_SEND_IM = 5;
+    static final int MENU_ITEM_EDIT = 6;
+    static final int MENU_ITEM_DELETE = 7;
+    static final int MENU_ITEM_TOGGLE_STAR = 8;
+
+    public static final int MENU_DIALER = 9;
+    public static final int MENU_NEW_CONTACT = 10;
+    public static final int MENU_DISPLAY_GROUP = 11;
+
+    private static final int SUBACTIVITY_NEW_CONTACT = 1;
+    
+    /** Mask for picker mode */
+    static final int MODE_MASK_PICKER = 0x80000000;
+    /** Mask for no presence mode */
+    static final int MODE_MASK_NO_PRESENCE = 0x40000000;
+    /** Mask for enabling list filtering */
+    static final int MODE_MASK_NO_FILTER = 0x20000000;
+    /** Mask for having a "create new contact" header in the list */
+    static final int MODE_MASK_CREATE_NEW = 0x10000000;
+
+    /** Unknown mode */
+    static final int MODE_UNKNOWN = 0;
+    /** Show members of the "Contacts" group */
+    static final int MODE_GROUP = 5;
+    /** Show all contacts sorted alphabetically */
+    static final int MODE_ALL_CONTACTS = 10;
+    /** Show all contacts with phone numbers, sorted alphabetically */
+    static final int MODE_WITH_PHONES = 15;
+    /** Show all starred contacts */
+    static final int MODE_STARRED = 20;
+    /** Show frequently contacted contacts */
+    static final int MODE_FREQUENT = 30;
+    /** Show starred and the frequent */
+    static final int MODE_STREQUENT = 35;
+    /** Show all contacts and pick them when clicking */
+    static final int MODE_PICK_CONTACT = 40 | MODE_MASK_PICKER;
+    /** Show all contacts as well as the option to create a new one */
+    static final int MODE_PICK_OR_CREATE_CONTACT = 42 | MODE_MASK_PICKER | MODE_MASK_CREATE_NEW;
+    /** Show all contacts and pick them when clicking, and allow creating a new contact */
+    static final int MODE_INSERT_OR_EDIT_CONTACT = 45 | MODE_MASK_PICKER | MODE_MASK_CREATE_NEW;
+    /** Show all phone numbers and pick them when clicking */
+    static final int MODE_PICK_PHONE = 50 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE;
+    /** Show all postal addresses and pick them when clicking */
+    static final int MODE_PICK_POSTAL =
+            55 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE | MODE_MASK_NO_FILTER;
+    /** Run a search query */
+    static final int MODE_QUERY = 60 | MODE_MASK_NO_FILTER;
+
+    static final int DEFAULT_MODE = MODE_ALL_CONTACTS;
+
+    static final String NAME_COLUMN = People.DISPLAY_NAME;
+    
+    static final String[] CONTACTS_PROJECTION = new String[] {
+        People._ID, // 0
+        NAME_COLUMN, // 1
+        People.NUMBER, // 2
+        People.TYPE, // 3
+        People.LABEL, // 4
+        People.STARRED, // 5
+        People.PRIMARY_PHONE_ID, // 6
+        People.PRIMARY_EMAIL_ID, // 7
+        People.PRESENCE_STATUS, // 8
+    };
+
+    static final String[] STREQUENT_PROJECTION = new String[] {
+        People._ID, // 0
+        NAME_COLUMN, // 1
+        People.NUMBER, // 2
+        People.TYPE, // 3
+        People.LABEL, // 4
+        People.STARRED, // 5
+        People.PRIMARY_PHONE_ID, // 6
+        People.PRIMARY_EMAIL_ID, // 7
+        People.PRESENCE_STATUS, // 8
+        People.TIMES_CONTACTED, // 9 (not displayed, but required for the order by to work)
+    };
+
+    static final String[] PHONES_PROJECTION = new String[] {
+        Phones._ID, // 0
+        NAME_COLUMN, // 1
+        Phones.NUMBER, // 2
+        Phones.TYPE, // 3
+        Phones.LABEL, // 4
+        Phones.STARRED, // 5
+    };
+
+    static final String[] CONTACT_METHODS_PROJECTION = new String[] {
+        ContactMethods._ID, // 0
+        NAME_COLUMN, // 1
+        ContactMethods.DATA, // 2
+        ContactMethods.TYPE, // 3
+        ContactMethods.LABEL, // 4
+        ContactMethods.STARRED, // 5
+    };
+
+    static final int ID_COLUMN_INDEX = 0;
+    static final int NAME_COLUMN_INDEX = 1;
+    static final int NUMBER_COLUMN_INDEX = 2;
+    static final int DATA_COLUMN_INDEX = 2;
+    static final int TYPE_COLUMN_INDEX = 3;
+    static final int LABEL_COLUMN_INDEX = 4;
+    static final int STARRED_COLUMN_INDEX = 5;
+    static final int PRIMARY_PHONE_ID_COLUMN_INDEX = 6;
+    static final int PRIMARY_EMAIL_ID_COLUMN_INDEX = 7;
+    static final int SERVER_STATUS_COLUMN_INDEX = 8;
+
+    
+    static final int DISPLAY_GROUP_INDEX_ALL_CONTACTS = 0;
+    static final int DISPLAY_GROUP_INDEX_ALL_CONTACTS_WITH_PHONES = 1;
+    static final int DISPLAY_GROUP_INDEX_MY_CONTACTS = 2;
+    
+    static final String SORT_ORDER = NAME_COLUMN + " COLLATE LOCALIZED ASC";
+    
+    private static final int QUERY_TOKEN = 42;
+
+    private static final String[] GROUPS_PROJECTION = new String[] {
+        Groups.SYSTEM_ID, // 0
+        Groups.NAME, // 1
+    };
+    private static final int GROUPS_COLUMN_INDEX_SYSTEM_ID = 0;
+    private static final int GROUPS_COLUMN_INDEX_NAME = 1;
+    
+    static final String GROUP_WITH_PHONES = "android_smartgroup_phone";
+
+    ContactItemListAdapter mAdapter;
+
+    private int mMode = DEFAULT_MODE;
+    // The current display group
+    private String mDisplayInfo;
+    private int mDisplayType;
+    // The current list of display groups, during selection from menu
+    private CharSequence[] mDisplayGroups;
+    // If true position 2 in mDisplayGroups is the MyContacts group
+    private boolean mDisplayGroupsIncludesMyContacts = false;
+
+    private int mDisplayGroupOriginalSelection;
+    private int mDisplayGroupCurrentSelection;
+    
+    private QueryHandler mQueryHandler;
+    private String mQuery;
+    private Uri mGroupFilterUri;
+    private Uri mGroupUri;
+    private boolean mJustCreated;
+
+    /**
+     * Used to keep track of the scroll state of the list.
+     */
+    private Parcelable mListState = null;
+    private boolean mListHasFocus;
+
+    private boolean mCreateShortcut;
+    private boolean mDefaultMode = false;
+
+    private class DeleteClickListener implements DialogInterface.OnClickListener {
+        private Uri mUri;
+
+        public DeleteClickListener(Uri uri) {
+            mUri = uri;
+        }
+
+        public void onClick(DialogInterface dialog, int which) {
+            getContentResolver().delete(mUri, null, null);
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Make sure the preferences are setup
+        PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
+
+        // Resolve the intent
+        final Intent intent = getIntent();
+
+        // Allow the title to be set to a custom String using an extra on the intent
+        String title = intent.getStringExtra(Contacts.Intents.UI.TITLE_EXTRA_KEY);
+        if (title != null) {
+            setTitle(title);
+        }
+
+        final String action = intent.getAction();
+        mMode = MODE_UNKNOWN;
+        
+        setContentView(R.layout.contacts_list_content);
+
+        if (UI.LIST_DEFAULT.equals(action)) {
+            mDefaultMode = true;
+            // When mDefaultMode is true the mode is set in onResume(), since the preferneces
+            // activity may change it whenever this activity isn't running
+        } else if (UI.LIST_GROUP_ACTION.equals(action)) {
+            mMode = MODE_GROUP;
+            String groupName = intent.getStringExtra(UI.GROUP_NAME_EXTRA_KEY);
+            if (TextUtils.isEmpty(groupName)) {
+                finish();
+                return;
+            }
+            buildUserGroupUris(groupName);
+        } else if (UI.LIST_ALL_CONTACTS_ACTION.equals(action)) {
+            mMode = MODE_ALL_CONTACTS;
+        } else if (UI.LIST_STARRED_ACTION.equals(action)) {
+            mMode = MODE_STARRED;
+        } else if (UI.LIST_FREQUENT_ACTION.equals(action)) {
+            mMode = MODE_FREQUENT;
+        } else if (UI.LIST_STREQUENT_ACTION.equals(action)) {
+            mMode = MODE_STREQUENT;
+        } else if (UI.LIST_CONTACTS_WITH_PHONES_ACTION.equals(action)) {
+            mMode = MODE_WITH_PHONES;
+        } else if (Intent.ACTION_PICK.equals(action)) {
+            // XXX These should be showing the data from the URI given in
+            // the Intent.
+            final String type = intent.resolveType(this);
+            if (People.CONTENT_TYPE.equals(type)) {
+                mMode = MODE_PICK_CONTACT;
+            } else if (Phones.CONTENT_TYPE.equals(type)) {
+                mMode = MODE_PICK_PHONE;
+            } else if (ContactMethods.CONTENT_POSTAL_TYPE.equals(type)) {
+                mMode = MODE_PICK_POSTAL;
+            }
+        } else if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
+            mMode = MODE_PICK_OR_CREATE_CONTACT;
+            mCreateShortcut = true;
+        } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
+            final String type = intent.resolveType(this);
+            if (People.CONTENT_ITEM_TYPE.equals(type)) {
+                mMode = MODE_PICK_OR_CREATE_CONTACT;
+            } else if (Phones.CONTENT_ITEM_TYPE.equals(type)) {
+                mMode = MODE_PICK_PHONE;
+            } else if (ContactMethods.CONTENT_POSTAL_ITEM_TYPE.equals(type)) {
+                mMode = MODE_PICK_POSTAL;
+            }
+        } else if (Intent.ACTION_INSERT_OR_EDIT.equals(action)) {
+            mMode = MODE_INSERT_OR_EDIT_CONTACT;
+        } else if (Intent.ACTION_SEARCH.equals(action)) {
+            // See if the suggestion was clicked with a search action key (call button)
+            if ("call".equals(intent.getStringExtra(SearchManager.ACTION_MSG))) {
+                String query = intent.getStringExtra(SearchManager.QUERY);
+                if (!TextUtils.isEmpty(query)) {
+                    Intent newIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+                            Uri.fromParts("tel", query, null));
+                    startActivity(newIntent);
+                }
+                finish();
+                return;
+            }
+            // Otherwise handle the more normal search case
+            mMode = MODE_QUERY;
+
+        // Since this is the filter activity it receives all intents
+        // dispatched from the SearchManager for security reasons
+        // so we need to re-dispatch from here to the intended target.
+        } else if (Intents.SEARCH_SUGGESTION_CLICKED.equals(action)) {
+            // See if the suggestion was clicked with a search action key (call button)
+            Intent newIntent;
+            if ("call".equals(intent.getStringExtra(SearchManager.ACTION_MSG))) {
+                newIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, intent.getData());
+            } else {
+                newIntent = new Intent(Intent.ACTION_VIEW, intent.getData());
+            }
+            startActivity(newIntent);
+            finish();
+            return;
+        } else if (Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED.equals(action)) {
+            Intent newIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, intent.getData());
+            startActivity(newIntent);
+            finish();
+            return;
+        } else if (Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED.equals(action)) {
+            String number = intent.getData().getSchemeSpecificPart();
+            Intent newIntent = new Intent(Intent.ACTION_INSERT, People.CONTENT_URI);
+            newIntent.putExtra(Intents.Insert.PHONE, number);
+            startActivity(newIntent);
+            finish();
+            return;
+        }
+
+        if (mMode == MODE_UNKNOWN) {
+            mMode = DEFAULT_MODE;
+        }
+
+/*
+        if (!mDefaultMode) {
+            findViewById(R.id.contact_group).banner.setVisibility(View.GONE);
+        }
+*/
+
+        // Setup the UI
+        final ListView list = getListView();
+        list.setFocusable(true);
+        list.setOnCreateContextMenuListener(this);
+        if ((mMode & MODE_MASK_NO_FILTER) != MODE_MASK_NO_FILTER) {
+            list.setTextFilterEnabled(true);
+        }
+
+        if ((mMode & MODE_MASK_CREATE_NEW) != 0) {
+            // Add the header for creating a new contact
+            final LayoutInflater inflater = getLayoutInflater();
+            View header = inflater.inflate(android.R.layout.simple_list_item_1, list, false);
+            TextView text = (TextView) header.findViewById(android.R.id.text1);
+            text.setText(R.string.pickerNewContactHeader);
+            list.addHeaderView(header);
+        }
+
+        // Set the proper empty string
+        setEmptyText();
+        
+        mAdapter = new ContactItemListAdapter(this, R.layout.contacts_list_item, null);
+        setListAdapter(mAdapter);
+
+        // We manually save/restore the listview state
+        list.setSaveEnabled(false);
+
+        mQueryHandler = new QueryHandler(this);
+        mJustCreated = true;
+    }
+
+    private void setEmptyText() {
+        TextView empty = (TextView) findViewById(android.R.id.empty);
+        switch (mMode) {
+            case MODE_GROUP:
+                if (Groups.GROUP_MY_CONTACTS.equals(mDisplayInfo)) {
+                    empty.setText(getString(R.string.groupEmpty,
+                            getText(R.string.groupNameMyContacts)));
+                } else {
+                    empty.setText(getString(R.string.groupEmpty, mDisplayInfo));
+                }
+                break;
+
+            case MODE_STARRED:
+            case MODE_STREQUENT:
+            case MODE_FREQUENT:
+                empty.setText(getText(R.string.noFavorites));
+                break;
+
+            case MODE_WITH_PHONES:
+                empty.setText(getText(R.string.noContactsWithPhoneNumbers));
+                break;
+
+            default:
+                empty.setText(getText(R.string.noContacts));
+                break;
+        }
+    }
+
+    /**
+     * Builds the URIs to query when displaying a user group
+     * 
+     * @param groupName the group being displayed
+     */
+    private void buildUserGroupUris(String groupName) {
+        mGroupFilterUri = Uri.parse("content://contacts/groups/name/" + groupName
+                + "/members/filter/");
+        mGroupUri = Uri.parse("content://contacts/groups/name/" + groupName + "/members");
+    }
+
+    /**
+     * Builds the URIs to query when displaying a system group
+     * 
+     * @param systemId the system group's ID 
+     */
+    private void buildSystemGroupUris(String systemId) {
+        mGroupFilterUri = Uri.parse("content://contacts/groups/system_id/" + systemId
+                + "/members/filter/");
+        mGroupUri = Uri.parse("content://contacts/groups/system_id/" + systemId + "/members");
+    }
+
+    /**
+     * Sets the mode when the request is for "default"
+     */
+    private void setDefaultMode() {
+        // Load the preferences
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+        
+        // Lookup the group to display
+        mDisplayType = prefs.getInt(ContactsPreferenceActivity.PREF_DISPLAY_TYPE,
+                ContactsPreferenceActivity.DISPLAY_TYPE_UNKNOWN);
+        switch (mDisplayType) {
+            case ContactsPreferenceActivity.DISPLAY_TYPE_ALL_WITH_PHONES: {
+                mMode = MODE_WITH_PHONES;
+                mDisplayInfo = null;
+                break;
+            }
+
+            case ContactsPreferenceActivity.DISPLAY_TYPE_SYSTEM_GROUP: {
+                String systemId = prefs.getString(
+                        ContactsPreferenceActivity.PREF_DISPLAY_INFO, null);
+                if (!TextUtils.isEmpty(systemId)) {
+                    // Display the selected system group
+                    mMode = MODE_GROUP;
+                    buildSystemGroupUris(systemId);
+                    mDisplayInfo = systemId;
+                } else {
+                    // No valid group is present, display everything
+                    mMode = MODE_WITH_PHONES;
+                    mDisplayInfo = null;
+                    mDisplayType = ContactsPreferenceActivity.DISPLAY_TYPE_ALL;
+                }
+                break;
+            }
+
+            case ContactsPreferenceActivity.DISPLAY_TYPE_USER_GROUP: {
+                String displayGroup = prefs.getString(
+                        ContactsPreferenceActivity.PREF_DISPLAY_INFO, null);
+                if (!TextUtils.isEmpty(displayGroup)) {
+                    // Display the selected user group
+                    mMode = MODE_GROUP;
+                    buildUserGroupUris(displayGroup);
+                    mDisplayInfo = displayGroup;
+                } else {
+                    // No valid group is present, display everything
+                    mMode = MODE_WITH_PHONES;
+                    mDisplayInfo = null;
+                    mDisplayType = ContactsPreferenceActivity.DISPLAY_TYPE_ALL;
+                }
+                break;
+            }
+
+            case ContactsPreferenceActivity.DISPLAY_TYPE_ALL: {
+                mMode = MODE_ALL_CONTACTS;
+                mDisplayInfo = null;
+                break;
+            }
+
+            default: {
+                // We don't know what to display, default to My Contacts
+                mMode = MODE_GROUP;
+                mDisplayType = ContactsPreferenceActivity.DISPLAY_TYPE_SYSTEM_GROUP;
+                buildSystemGroupUris(Groups.GROUP_MY_CONTACTS);
+                mDisplayInfo = Groups.GROUP_MY_CONTACTS;
+                break;
+            }
+        }
+
+        // Update the empty text view with the proper string, as the group may have changed
+        setEmptyText();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        boolean runQuery = true;
+        Activity parent = getParent();
+        
+        // Do this before setting the filter. The filter thread relies
+        // on some state that is initialized in setDefaultMode
+        if (mDefaultMode) {
+            // If we're in default mode we need to possibly reset the mode due to a change
+            // in the preferences activity while we weren't running
+            setDefaultMode();
+        }
+        
+        // See if we were invoked with a filter
+        if (parent != null && parent instanceof DialtactsActivity) {
+            String filterText = ((DialtactsActivity) parent).getAndClearFilterText();
+            if (filterText != null && filterText.length() > 0) {
+                getListView().setFilterText(filterText);
+                // Don't start a new query since it will conflict with the filter
+                runQuery = false;
+            } else if (mJustCreated) {
+                getListView().clearTextFilter();
+            }
+        }
+
+        if (runQuery) {
+            // Calling requery here may cause an ANR, so always do the async query
+            startQuery();
+        }
+        mJustCreated = false;
+    }
+    
+    private void updateGroup() {
+        if (mDefaultMode) {
+            setDefaultMode();
+        }
+
+        // Calling requery here may cause an ANR, so always do the async query
+        startQuery();
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle icicle) {
+        super.onSaveInstanceState(icicle);
+        // Save list state in the bundle so we can restore it after the QueryHandler has run
+        icicle.putParcelable(LIST_STATE_KEY, mList.onSaveInstanceState());
+        icicle.putBoolean(FOCUS_KEY, mList.hasFocus());
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle icicle) {
+        super.onRestoreInstanceState(icicle);
+        // Retrieve list state. This will be applied after the QueryHandler has run
+        mListState = icicle.getParcelable(LIST_STATE_KEY);
+        mListHasFocus = icicle.getBoolean(FOCUS_KEY);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        // We don't want the list to display the empty state, since when we resume it will still
+        // be there and show up while the new query is happening. After the async query finished
+        // in response to onResume() setLoading(false) will be called.
+        mAdapter.setLoading(true);
+        mAdapter.changeCursor(null);
+
+        if (mMode == MODE_QUERY) {
+            // Make sure the search box is closed
+            SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
+            searchManager.stopSearch();
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // If Contacts was invoked by another Activity simply as a way of
+        // picking a contact, don't show the options menu
+        if ((mMode & MODE_MASK_PICKER) == MODE_MASK_PICKER) {
+            return false;
+        }
+
+        menu.add(0, MENU_NEW_CONTACT, 0, R.string.menu_newContact)
+                .setIcon(android.R.drawable.ic_menu_add)
+                .setIntent(new Intent(Intents.Insert.ACTION, People.CONTENT_URI))
+                .setAlphabeticShortcut('n');
+        if (isChild()) {
+            menu.add(0, 0, 0, R.string.menu_preferences)
+                    .setIcon(android.R.drawable.ic_menu_preferences)
+                    .setIntent(new Intent(this, ContactsPreferenceActivity.class));
+        }
+        if (mDefaultMode) {
+            menu.add(0, MENU_DISPLAY_GROUP, 0, R.string.menu_displayGroup)
+                    .setIcon(R.drawable.ic_menu_allfriends);
+        }
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    /*
+     * Implements the handler for display group selection.
+     */
+    public void onClick(DialogInterface dialogInterface, int which) {
+        if (which == DialogInterface.BUTTON1) {
+            // The OK button was pressed
+            if (mDisplayGroupOriginalSelection != mDisplayGroupCurrentSelection) {
+                // Set the group to display
+                if (mDisplayGroupCurrentSelection == DISPLAY_GROUP_INDEX_ALL_CONTACTS) {
+                    // Display all
+                    mDisplayType = ContactsPreferenceActivity.DISPLAY_TYPE_ALL;
+                    mDisplayInfo = null;
+                } else if (mDisplayGroupCurrentSelection
+                        == DISPLAY_GROUP_INDEX_ALL_CONTACTS_WITH_PHONES) {
+                    // Display all with phone numbers
+                    mDisplayType = ContactsPreferenceActivity.DISPLAY_TYPE_ALL_WITH_PHONES;
+                    mDisplayInfo = null;
+                } else if (mDisplayGroupsIncludesMyContacts &&
+                        mDisplayGroupCurrentSelection == DISPLAY_GROUP_INDEX_MY_CONTACTS) {
+                    mDisplayType = ContactsPreferenceActivity.DISPLAY_TYPE_SYSTEM_GROUP;
+                    mDisplayInfo = Groups.GROUP_MY_CONTACTS;
+                } else {
+                    mDisplayType = ContactsPreferenceActivity.DISPLAY_TYPE_USER_GROUP;
+                    mDisplayInfo = mDisplayGroups[mDisplayGroupCurrentSelection].toString();
+                }
+
+                // Save the changes to the preferences
+                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+                prefs.edit()
+                        .putInt(ContactsPreferenceActivity.PREF_DISPLAY_TYPE, mDisplayType)
+                        .putString(ContactsPreferenceActivity.PREF_DISPLAY_INFO, mDisplayInfo)
+                        .commit();
+
+                // Update the display state
+                updateGroup();
+            }
+        } else {
+            // A list item was selected, cache the position
+            mDisplayGroupCurrentSelection = which;
+        }
+    }
+    
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == MENU_DISPLAY_GROUP) {
+            AlertDialog.Builder builder = new AlertDialog.Builder(this)
+                .setTitle(R.string.select_group_title)
+                .setPositiveButton(android.R.string.ok, this)
+                .setNegativeButton(android.R.string.cancel, null);
+            
+            setGroupEntries(builder);
+            
+            builder.show();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode,
+            Intent data) {
+        switch (requestCode) {
+            case SUBACTIVITY_NEW_CONTACT:
+                if (resultCode == RESULT_OK) {
+                    // Contact was created, pass it back
+                    returnPickerResult(data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME),
+                            data.getData());
+                }
+        }
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+        // If Contacts was invoked by another Activity simply as a way of
+        // picking a contact, don't show the context menu
+        if ((mMode & MODE_MASK_PICKER) == MODE_MASK_PICKER) {
+            return;
+        }
+
+        AdapterView.AdapterContextMenuInfo info;
+        try {
+             info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+        } catch (ClassCastException e) {
+            Log.e(TAG, "bad menuInfo", e);
+            return;
+        }
+
+        Cursor cursor = (Cursor) getListAdapter().getItem(info.position);
+        if (cursor == null) {
+            // For some reason the requested item isn't available, do nothing
+            return;
+        }
+        long id = info.id;
+        Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, id);
+
+        // Setup the menu header
+        menu.setHeaderTitle(cursor.getString(NAME_COLUMN_INDEX));
+
+        // View contact details
+        menu.add(0, MENU_ITEM_VIEW_CONTACT, 0, R.string.menu_viewContact)
+                .setIntent(new Intent(Intent.ACTION_VIEW, personUri));
+
+        // Calling contact
+        long phoneId = cursor.getLong(PRIMARY_PHONE_ID_COLUMN_INDEX);
+        if (phoneId > 0) {
+            // Get the display label for the number
+            CharSequence label = cursor.getString(LABEL_COLUMN_INDEX);
+            int type = cursor.getInt(TYPE_COLUMN_INDEX);
+            label = Phones.getDisplayLabel(this, type, label);
+            Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+                    ContentUris.withAppendedId(Phones.CONTENT_URI, phoneId));
+            menu.add(0, MENU_ITEM_CALL, 0, String.format(getString(R.string.menu_callNumber), label))
+                    .setIntent(intent);
+
+            // Send SMS item
+            menu.add(0, MENU_ITEM_SEND_SMS, 0, R.string.menu_sendSMS)
+                    .setIntent(new Intent(Intent.ACTION_SENDTO,
+                            Uri.fromParts("sms", cursor.getString(NUMBER_COLUMN_INDEX), null)));
+        }
+
+        // Star toggling
+        int starState = cursor.getInt(STARRED_COLUMN_INDEX);
+        if (starState == 0) {
+            menu.add(0, MENU_ITEM_TOGGLE_STAR, 0, R.string.menu_addStar);
+        } else {
+            menu.add(0, MENU_ITEM_TOGGLE_STAR, 0, R.string.menu_removeStar);
+        }
+
+        // Contact editing
+        menu.add(0, MENU_ITEM_EDIT, 0, R.string.menu_editContact)
+                .setIntent(new Intent(Intent.ACTION_EDIT, personUri));
+        menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact);
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        AdapterView.AdapterContextMenuInfo info;
+        try {
+             info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+        } catch (ClassCastException e) {
+            Log.e(TAG, "bad menuInfo", e);
+            return false;
+        }
+
+        Cursor cursor = (Cursor) getListAdapter().getItem(info.position);
+
+        switch (item.getItemId()) {
+            case MENU_ITEM_TOGGLE_STAR: {
+                // Toggle the star
+                ContentValues values = new ContentValues(1);
+                values.put(People.STARRED, cursor.getInt(STARRED_COLUMN_INDEX) == 0 ? 1 : 0);
+                Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI,
+                        cursor.getInt(ID_COLUMN_INDEX));
+                getContentResolver().update(personUri, values, null, null);
+                return true;
+            }
+
+            case MENU_ITEM_DELETE: {
+                // Get confirmation
+                Uri uri = ContentUris.withAppendedId(People.CONTENT_URI,
+                        cursor.getLong(ID_COLUMN_INDEX));
+                //TODO make this dialog persist across screen rotations
+                new AlertDialog.Builder(ContactsListActivity.this)
+                    .setTitle(R.string.deleteConfirmation_title)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setMessage(R.string.deleteConfirmation)
+                    .setNegativeButton(R.string.noButton, null)
+                    .setPositiveButton(R.string.yesButton, new DeleteClickListener(uri))
+                    .setCancelable(false)
+                    .show();
+                return true;
+            }
+        }
+
+        return super.onContextItemSelected(item);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_CALL: {
+                if (callSelection()) {
+                    return true;
+                }
+                break;
+            }
+
+            case KeyEvent.KEYCODE_DEL: {
+                Object o = getListView().getSelectedItem();
+                if (o != null) {
+                    Cursor cursor = (Cursor) o;
+                    Uri uri = ContentUris.withAppendedId(People.CONTENT_URI,
+                            cursor.getLong(ID_COLUMN_INDEX));
+                    //TODO make this dialog persist across screen rotations
+                    new AlertDialog.Builder(ContactsListActivity.this)
+                        .setTitle(R.string.deleteConfirmation_title)
+                        .setIcon(android.R.drawable.ic_dialog_alert)
+                        .setMessage(R.string.deleteConfirmation)
+                        .setNegativeButton(R.string.noButton, null)
+                        .setPositiveButton(R.string.yesButton, new DeleteClickListener(uri))
+                        .setCancelable(false)
+                        .show();
+                    return true;
+                }
+                break;
+            }
+        }
+
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        if (mMode == MODE_INSERT_OR_EDIT_CONTACT) {
+            Intent intent;
+            if (position == 0) {
+                // Insert
+                intent = new Intent(Intent.ACTION_INSERT, People.CONTENT_URI);
+            } else {
+                // Edit
+                intent = new Intent(Intent.ACTION_EDIT,
+                        ContentUris.withAppendedId(People.CONTENT_URI, id));
+            }
+            intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+            final Bundle extras = getIntent().getExtras();
+            if (extras != null) {
+                intent.putExtras(extras);
+            }
+            startActivity(intent);
+            finish();
+        } else if (id != -1) {
+            if ((mMode & MODE_MASK_PICKER) == 0) {
+                Intent intent = new Intent(Intent.ACTION_VIEW,
+                        ContentUris.withAppendedId(People.CONTENT_URI, id));
+                startActivity(intent);
+            } else if (mMode == MODE_PICK_CONTACT 
+                    || mMode == MODE_PICK_OR_CREATE_CONTACT) {
+                Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, id);
+                if (mCreateShortcut) {
+                    // Subtract one if we have Create Contact at the top
+                    Cursor c = (Cursor) mAdapter.getItem(position
+                            - (mMode == MODE_PICK_OR_CREATE_CONTACT? 1:0));
+                    returnPickerResult(c.getString(NAME_COLUMN_INDEX), uri);
+                } else {
+                    returnPickerResult(null, uri);
+                }
+            } else if (mMode == MODE_PICK_PHONE) {
+                setResult(RESULT_OK, new Intent().setData(
+                        ContentUris.withAppendedId(Phones.CONTENT_URI, id)));
+                finish();
+            } else if (mMode == MODE_PICK_POSTAL) {
+                setResult(RESULT_OK, new Intent().setData(
+                        ContentUris.withAppendedId(ContactMethods.CONTENT_URI, id)));
+                finish();
+            }
+        } else if ((mMode & MODE_MASK_CREATE_NEW) == MODE_MASK_CREATE_NEW
+                && position == 0) {
+            Intent newContact = new Intent(Intents.Insert.ACTION, People.CONTENT_URI);
+            startActivityForResult(newContact, SUBACTIVITY_NEW_CONTACT);
+        } else {
+            signalError();
+        }
+    }
+
+    private void returnPickerResult(String name, Uri uri) {
+        final Intent intent = new Intent();
+    
+        if (mCreateShortcut) {
+            Intent shortcutIntent = new Intent(Intent.ACTION_VIEW, uri);
+            shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
+            intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
+            final Bitmap icon = People.loadContactPhoto(this, uri, 0, null);
+            if (icon != null) {
+                intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
+            } else {
+                intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
+                        Intent.ShortcutIconResource.fromContext(this,
+                                R.drawable.ic_launcher_contacts));
+            }
+            setResult(RESULT_OK, intent);
+        } else {
+            setResult(RESULT_OK, intent.setData(uri));
+        }
+        finish();
+    }
+
+    String[] getProjection() {
+        switch (mMode) {
+            case MODE_GROUP:
+            case MODE_ALL_CONTACTS:
+            case MODE_WITH_PHONES:
+            case MODE_PICK_CONTACT:
+            case MODE_PICK_OR_CREATE_CONTACT:
+            case MODE_QUERY:
+            case MODE_STARRED:
+            case MODE_FREQUENT:
+            case MODE_INSERT_OR_EDIT_CONTACT:
+                return CONTACTS_PROJECTION;
+
+            case MODE_STREQUENT:
+                return STREQUENT_PROJECTION;
+
+            case MODE_PICK_PHONE:
+                return PHONES_PROJECTION;
+
+            case MODE_PICK_POSTAL:
+                return CONTACT_METHODS_PROJECTION;
+        }
+        return null;
+    }
+
+    private Uri getPeopleFilterUri(String filter) {
+        if (!TextUtils.isEmpty(filter)) {
+            return Uri.withAppendedPath(People.CONTENT_FILTER_URI, Uri.encode(filter));
+        } else {
+            return People.CONTENT_URI;
+        }
+    }
+
+    void startQuery() {
+        mAdapter.setLoading(true);
+        
+        // Cancel any pending queries
+        mQueryHandler.cancelOperation(QUERY_TOKEN);
+
+        // Kick off the new query
+        switch (mMode) {
+            case MODE_GROUP:
+                mQueryHandler.startQuery(QUERY_TOKEN, null,
+                        mGroupUri, CONTACTS_PROJECTION, null, null, SORT_ORDER);
+                break;
+
+            case MODE_ALL_CONTACTS:
+            case MODE_PICK_CONTACT:
+            case MODE_PICK_OR_CREATE_CONTACT:
+            case MODE_INSERT_OR_EDIT_CONTACT:
+                mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION,
+                        null, null, SORT_ORDER);
+                break;
+
+            case MODE_WITH_PHONES:
+                mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION,
+                        People.PRIMARY_PHONE_ID + " IS NOT NULL", null, People.DEFAULT_SORT_ORDER);
+                break;
+
+            case MODE_QUERY: {
+                mQuery = getIntent().getStringExtra(SearchManager.QUERY);
+                mQueryHandler.startQuery(QUERY_TOKEN, null, getPeopleFilterUri(mQuery),
+                        CONTACTS_PROJECTION, null, null, SORT_ORDER);
+                break;
+            }
+
+            case MODE_STARRED:
+                mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION,
+                        People.STARRED + "=1", null, SORT_ORDER);
+                break;
+
+            case MODE_FREQUENT:
+                mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION,
+                        People.TIMES_CONTACTED + " > 0", null,
+                        People.TIMES_CONTACTED + " DESC, " + NAME_COLUMN + " ASC");
+                break;
+
+            case MODE_STREQUENT:
+                mQueryHandler.startQuery(QUERY_TOKEN, null,
+                        Uri.withAppendedPath(People.CONTENT_URI, "strequent"), STREQUENT_PROJECTION,
+                        null, null, null);
+                break;
+
+            case MODE_PICK_PHONE:
+                mQueryHandler.startQuery(QUERY_TOKEN, null, Phones.CONTENT_URI, PHONES_PROJECTION,
+                        null, null, SORT_ORDER);
+                break;
+
+            case MODE_PICK_POSTAL:
+                mQueryHandler.startQuery(QUERY_TOKEN, null, ContactMethods.CONTENT_URI,
+                        CONTACT_METHODS_PROJECTION,
+                        ContactMethods.KIND + "=" + Contacts.KIND_POSTAL, null, SORT_ORDER);
+                break;
+        }
+    }
+
+    /**
+     * Called from a background thread to do the filter and return the resulting cursor.
+     * 
+     * @param filter the text that was entered to filter on
+     * @return a cursor with the results of the filter
+     */
+    Cursor doFilter(String filter) {
+        final ContentResolver resolver = getContentResolver();
+
+        switch (mMode) {
+            case MODE_GROUP: {
+                Uri uri;
+                if (TextUtils.isEmpty(filter)) {
+                    uri = mGroupUri;
+                } else {
+                    uri = Uri.withAppendedPath(mGroupFilterUri, Uri.encode(filter));
+                }
+                return resolver.query(uri, CONTACTS_PROJECTION, null, null,
+                        People.DEFAULT_SORT_ORDER);
+            }
+
+            case MODE_ALL_CONTACTS:
+            case MODE_PICK_CONTACT:
+            case MODE_PICK_OR_CREATE_CONTACT:
+            case MODE_INSERT_OR_EDIT_CONTACT: {
+                return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION, null, null,
+                        SORT_ORDER);
+            }
+
+            case MODE_WITH_PHONES: {
+                return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION,
+                        People.PRIMARY_PHONE_ID + " IS NOT NULL", null, SORT_ORDER);
+            }
+
+            case MODE_STARRED: {
+                return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION,
+                        People.STARRED + "=1", null, SORT_ORDER);
+            }
+
+            case MODE_FREQUENT: {
+                return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION,
+                        People.TIMES_CONTACTED + " > 0", null,
+                        People.TIMES_CONTACTED + " DESC, " + NAME_COLUMN + " ASC");
+            }
+
+            case MODE_STREQUENT: {
+                Uri uri;
+                if (!TextUtils.isEmpty(filter)) {
+                    uri = Uri.withAppendedPath(People.CONTENT_URI, "strequent/filter/"
+                            + Uri.encode(filter));
+                } else {
+                    uri = Uri.withAppendedPath(People.CONTENT_URI, "strequent");
+                }
+                return resolver.query(uri, STREQUENT_PROJECTION, null, null, null);
+            }
+
+            case MODE_PICK_PHONE: {
+                Uri uri;
+                if (!TextUtils.isEmpty(filter)) {
+                    uri = Uri.withAppendedPath(Phones.CONTENT_URI, "filter_name/"
+                            + Uri.encode(filter));
+                } else {
+                    uri = Phones.CONTENT_URI;
+                }
+                return resolver.query(uri, PHONES_PROJECTION, null, null, SORT_ORDER);
+            }
+        }
+        throw new UnsupportedOperationException("filtering not allowed in mode " + mMode);
+    }
+
+    /**
+     * Calls the currently selected list item.
+     * @return true if the call was initiated, false otherwise
+     */
+    boolean callSelection() {
+        ListView list = getListView();
+        if (list.hasFocus()) {
+            Cursor cursor = (Cursor) list.getSelectedItem();
+            if (cursor != null) {
+                long phoneId = cursor.getLong(PRIMARY_PHONE_ID_COLUMN_INDEX);
+                if (phoneId == 0) {
+                    // There is no phone number.
+                    signalError();
+                    return false;
+                }
+                Uri uri = ContentUris.withAppendedId(Phones.CONTENT_URI, phoneId);
+                Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, uri);
+                startActivity(intent);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Signal an error to the user.
+     */
+    void signalError() {
+        //TODO play an error beep or something...
+    }
+
+    Cursor getItemForView(View view) {
+        ListView listView = getListView();
+        int index = listView.getPositionForView(view);
+        if (index < 0) {
+            return null;
+        }
+        return (Cursor) listView.getAdapter().getItem(index);
+    }
+
+    private void setGroupEntries(AlertDialog.Builder builder) {
+        boolean syncEverything;
+        // For now we only support a single account and the UI doesn't know what
+        // the account name is, so we're using a global setting for SYNC_EVERYTHING.
+        // Some day when we add multiple accounts to the UI this should use the per
+        // account setting.
+        String value = Contacts.Settings.getSetting(getContentResolver(), null,
+                Contacts.Settings.SYNC_EVERYTHING);
+        if (value == null) {
+            // If nothing is set yet we default to syncing everything
+            syncEverything = true;
+        } else {
+            syncEverything = !TextUtils.isEmpty(value) && !"0".equals(value);
+        }
+
+        Cursor cursor;
+        if (!syncEverything) {
+            cursor = getContentResolver().query(Groups.CONTENT_URI, GROUPS_PROJECTION,
+                    Groups.SHOULD_SYNC + " != 0", null, Groups.DEFAULT_SORT_ORDER);
+        } else {
+            cursor = getContentResolver().query(Groups.CONTENT_URI, GROUPS_PROJECTION,
+                    null, null, Groups.DEFAULT_SORT_ORDER);
+        }
+        try {
+            ArrayList<CharSequence> groups = new ArrayList<CharSequence>();
+            ArrayList<CharSequence> prefStrings = new ArrayList<CharSequence>();
+
+            // Add All Contacts
+            groups.add(DISPLAY_GROUP_INDEX_ALL_CONTACTS, getString(R.string.showAllGroups));
+            prefStrings.add("");
+            
+            // Add Contacts with phones
+            groups.add(DISPLAY_GROUP_INDEX_ALL_CONTACTS_WITH_PHONES,
+                    getString(R.string.groupNameWithPhones));
+            prefStrings.add(GROUP_WITH_PHONES);
+            
+            int i = 3;
+            int currentIndex = DISPLAY_GROUP_INDEX_ALL_CONTACTS;
+            while (cursor.moveToNext()) {
+                String systemId = cursor.getString(GROUPS_COLUMN_INDEX_SYSTEM_ID);
+                String name = cursor.getString(GROUPS_COLUMN_INDEX_NAME);
+                if (cursor.isNull(GROUPS_COLUMN_INDEX_SYSTEM_ID)
+                        && !Groups.GROUP_MY_CONTACTS.equals(systemId)) {
+                    // All groups that aren't My Contacts, since that one is localized on the phone
+                    groups.add(name);
+                    if (name.equals(mDisplayInfo)) {
+                        currentIndex = i;
+                    }
+                    i++;
+                } else {
+                    // The My Contacts group
+                    groups.add(DISPLAY_GROUP_INDEX_MY_CONTACTS,
+                            getString(R.string.groupNameMyContacts));
+                    if (mDisplayType == ContactsPreferenceActivity.DISPLAY_TYPE_SYSTEM_GROUP
+                            && Groups.GROUP_MY_CONTACTS.equals(mDisplayInfo)) {
+                        currentIndex = DISPLAY_GROUP_INDEX_MY_CONTACTS;
+                    }
+                    mDisplayGroupsIncludesMyContacts = true;
+                }
+            }
+            if (mMode == MODE_ALL_CONTACTS) {
+                currentIndex = DISPLAY_GROUP_INDEX_ALL_CONTACTS;
+            } else if (mMode == MODE_WITH_PHONES) {
+                currentIndex = DISPLAY_GROUP_INDEX_ALL_CONTACTS_WITH_PHONES;
+            }
+            mDisplayGroups = groups.toArray(new CharSequence[groups.size()]);
+            builder.setSingleChoiceItems(mDisplayGroups,
+                    currentIndex, this);
+            mDisplayGroupOriginalSelection = currentIndex;
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private final class QueryHandler extends AsyncQueryHandler {
+        public QueryHandler(Context context) {
+            super(context.getContentResolver());
+        }
+
+        @Override
+        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+            if (!isFinishing()) {
+                mAdapter.setLoading(false);
+                mAdapter.changeCursor(cursor);
+    
+                // Now that the cursor is populated again, it's possible to restore the list state
+                if (mListState != null) {
+                    mList.onRestoreInstanceState(mListState);
+                    if (mListHasFocus) {
+                        mList.requestFocus();
+                    }
+                    mListHasFocus = false;
+                    mListState = null;
+                }
+            } else {
+                cursor.close();
+            }
+        }
+    }
+
+    final static class ContactListItemCache {
+        public TextView nameView;
+        public CharArrayBuffer nameBuffer = new CharArrayBuffer(128);
+        public TextView labelView;
+        public CharArrayBuffer labelBuffer = new CharArrayBuffer(128);
+        public TextView numberView;
+        public CharArrayBuffer numberBuffer = new CharArrayBuffer(128);
+    }
+
+    private final class ContactItemListAdapter extends ResourceCursorAdapter 
+            implements FastScrollView.SectionIndexer {
+        
+        private String [] mAlphabet;
+        private AlphabetIndexer mIndexer;
+        private boolean mLoading = true;
+        private CharSequence mUnknownNameText;
+        private CharSequence[] mLocalizedLabels;
+
+        public ContactItemListAdapter(Context context, int resource, Cursor cursor) {
+            super(context, resource, cursor);
+            getAlphabet(context);
+            if (cursor != null) {
+                mIndexer = new AlphabetIndexer(cursor, NAME_COLUMN_INDEX, mAlphabet);
+            }
+            mUnknownNameText = context.getText(android.R.string.unknownName);
+            switch (mMode) {
+                case MODE_PICK_POSTAL:
+                    mLocalizedLabels = EditContactActivity.getLabelsForKind(mContext,
+                            Contacts.KIND_POSTAL);
+                    break;
+                default:
+                    mLocalizedLabels = EditContactActivity.getLabelsForKind(mContext,
+                            Contacts.KIND_PHONE);
+                    break;
+            }
+        }
+
+        public void setLoading(boolean loading) {
+            mLoading = loading;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            if (mLoading) {
+                // We don't want the empty state to show when loading.
+                return false;
+            } else {
+                return super.isEmpty();
+            }
+        }
+        
+        private void getAlphabet(Context context) {
+            String alphabetString = context.getResources().getString(R.string.alphabet);
+            mAlphabet = new String[alphabetString.length()];
+            for (int i = 0; i < mAlphabet.length; i++) {
+                mAlphabet[i] = String.valueOf(alphabetString.charAt(i));
+            }
+        }
+
+        @Override
+        public View newView(Context context, Cursor cursor, ViewGroup parent) {
+            final View view = super.newView(context, cursor, parent);
+
+            final ContactListItemCache cache = new ContactListItemCache();
+            cache.nameView = (TextView) view.findViewById(R.id.name);
+            cache.labelView = (TextView) view.findViewById(R.id.label);
+            cache.labelView.setCompoundDrawablePadding(3);
+            cache.numberView = (TextView) view.findViewById(R.id.number);
+            view.setTag(cache);
+
+            return view;
+        }
+
+        @Override
+        public void bindView(View view, Context context, Cursor cursor) {
+            final ContactListItemCache cache = (ContactListItemCache) view.getTag();
+            
+            // Set the name           
+            cursor.copyStringToBuffer(NAME_COLUMN_INDEX, cache.nameBuffer);
+            int size = cache.nameBuffer.sizeCopied;
+            if (size != 0) {
+                cache.nameView.setText(cache.nameBuffer.data, 0, size);
+            } else {
+                cache.nameView.setText(mUnknownNameText);
+            }
+            
+            // Set the phone number
+            TextView numberView = cache.numberView;
+            cursor.copyStringToBuffer(NUMBER_COLUMN_INDEX, cache.numberBuffer);
+            size = cache.numberBuffer.sizeCopied;
+            if (size != 0) {
+                numberView.setText(cache.numberBuffer.data, 0, size);
+                numberView.setVisibility(View.VISIBLE);
+            } else {
+                numberView.setVisibility(View.GONE);
+            }
+
+            // Set the label
+            TextView labelView = cache.labelView;
+            if (!cursor.isNull(TYPE_COLUMN_INDEX)) {
+                int type = cursor.getInt(TYPE_COLUMN_INDEX);
+
+                if (type != People.Phones.TYPE_CUSTOM) {
+                    try {
+                        labelView.setText(mLocalizedLabels[type - 1]);
+                    } catch (ArrayIndexOutOfBoundsException e) {
+                        labelView.setText(mLocalizedLabels[People.Phones.TYPE_HOME - 1]);
+                    }
+                } else {
+                    cursor.copyStringToBuffer(LABEL_COLUMN_INDEX, cache.labelBuffer);
+                    // Don't check size, if it's zero just don't show anything
+                    labelView.setText(cache.labelBuffer.data, 0, cache.labelBuffer.sizeCopied);
+                }
+            } else {
+                // Set the text to a length of 0
+                labelView.setText(cache.labelBuffer.data, 0, 0);
+            }
+            // Set the proper icon in the label view
+            if (mMode != MODE_STREQUENT) {
+                if ((mMode & MODE_MASK_NO_PRESENCE) == 0) {
+                    int serverStatus;
+                    if (!cursor.isNull(SERVER_STATUS_COLUMN_INDEX)) {
+                        serverStatus = cursor.getInt(SERVER_STATUS_COLUMN_INDEX);
+                        labelView.setCompoundDrawablesWithIntrinsicBounds(
+                                getResources().getDrawable(
+                                        Presence.getPresenceIconResourceId(serverStatus)),
+                                null, null, null);
+                    } else {
+                        labelView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
+                    }
+                } else {
+                    labelView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
+                }
+            } else {
+                if (cursor.getInt(STARRED_COLUMN_INDEX) != 0) {
+                    labelView.setCompoundDrawablesWithIntrinsicBounds(
+                            getResources().getDrawable(R.drawable.star_on),
+                            null, null, null);
+                } else {
+                    labelView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
+                }
+            }
+        }
+
+        @Override
+        public void changeCursor(Cursor cursor) {
+            super.changeCursor(cursor);
+            updateIndexer(cursor);
+        }
+        
+        private void updateIndexer(Cursor cursor) {
+            if (mIndexer == null) {
+                mIndexer = new AlphabetIndexer(cursor, NAME_COLUMN_INDEX, mAlphabet);
+            } else {
+                mIndexer.setCursor(cursor);
+            }
+        }
+        
+        /**
+         * Run the query on a helper thread. Beware that this code does not run
+         * on the main UI thread!
+         */
+        @Override
+        public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
+            return doFilter(constraint.toString());
+        }
+        
+        public Object [] getSections() {
+            if (mMode == MODE_STREQUENT) {
+                return new String[] { " " };
+            } else {
+                return mAlphabet;
+            }
+        }
+        
+        public int getPositionForSection(int sectionIndex) {
+            if (mMode == MODE_STREQUENT) {
+                return 0;
+            }
+
+            if (mIndexer == null) {
+                Cursor cursor = mAdapter.getCursor();
+                if (cursor == null) {
+                    // No cursor, the section doesn't exist so just return 0
+                    return 0;
+                }
+                mIndexer = new AlphabetIndexer(cursor, NAME_COLUMN_INDEX, mAlphabet);
+            }
+
+            return mIndexer.indexOf(sectionIndex);
+        }
+        
+        public int getSectionForPosition(int position) {
+            return 0;
+        }        
+    }
+}
+
+
diff --git a/src/com/android/contacts/ContactsPreferenceActivity.java b/src/com/android/contacts/ContactsPreferenceActivity.java
new file mode 100644
index 0000000..b625fe5
--- /dev/null
+++ b/src/com/android/contacts/ContactsPreferenceActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 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.android.contacts;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+public final class ContactsPreferenceActivity extends PreferenceActivity {
+    /**
+     * The type of data to display in the main contacts list. 
+     */
+    static final String PREF_DISPLAY_TYPE = "display_system_group";
+
+    /** Unknown display type. */
+    static final int DISPLAY_TYPE_UNKNOWN = -1;
+    /** Display all contacts */
+    static final int DISPLAY_TYPE_ALL = 0;
+    /** Display all contacts that have phone numbers */
+    static final int DISPLAY_TYPE_ALL_WITH_PHONES = 1;
+    /** Display a system group */
+    static final int DISPLAY_TYPE_SYSTEM_GROUP = 2;
+    /** Display a user group */
+    static final int DISPLAY_TYPE_USER_GROUP = 3;
+
+    /**
+     * Info about what to display. If {@link #PREF_DISPLAY_TYPE}
+     * is {@link #DISPLAY_TYPE_SYSTEM_GROUP} then this will be the system id.
+     * If {@link #PREF_DISPLAY_TYPE} is {@link #DISPLAY_TYPE_USER_GROUP} then this will
+     * be the group name.
+     */ 
+    static final String PREF_DISPLAY_INFO = "display_group";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        // Load the preferences from an XML resource
+        addPreferencesFromResource(R.xml.preferences);
+    }
+
+}
diff --git a/src/com/android/contacts/DialtactsActivity.java b/src/com/android/contacts/DialtactsActivity.java
new file mode 100644
index 0000000..00df1c1
--- /dev/null
+++ b/src/com/android/contacts/DialtactsActivity.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2008 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.android.contacts;
+
+import android.app.TabActivity;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Contacts;
+import android.provider.CallLog.Calls;
+import android.provider.Contacts.Intents.UI;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Window;
+import android.widget.TabHost;
+import com.android.internal.telephony.ITelephony;
+
+/**
+ * The dialer activity that has one tab with the virtual 12key dialer,
+ * and another tab with recent calls in it. This is the container and the tabs
+ * are embedded using intents.
+ */
+public class DialtactsActivity extends TabActivity {
+    private static final String TAG = "Dailtacts";
+
+    public static final String EXTRA_IGNORE_STATE = "ignore-state";
+
+    private static final int FAVORITES_STARRED = 1;
+    private static final int FAVORITES_FREQUENT = 2;
+    private static final int FAVORITES_STREQUENT = 3;
+    
+    /** Defines what is displayed in the right tab */
+    private static final int FAVORITES_TAB_MODE = FAVORITES_STREQUENT;
+
+    protected TabHost mTabHost;
+    
+    private String mFilterText;
+    
+    private Uri mDialUri;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final Intent intent = getIntent();
+        fixIntent(intent);
+        
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        setContentView(R.layout.dialer_activity);
+
+        mTabHost = getTabHost();
+
+        // Setup the tabs
+        setupDialerTab();
+        setupCallLogTab();
+        setupContactsTab();
+        setupFavoritesTab();
+
+        setCurrentTab(intent);
+        
+        if (intent.getAction().equals(Contacts.Intents.UI.FILTER_CONTACTS_ACTION) 
+                && icicle == null) {
+            setupFilterText(intent);
+        }
+    }
+
+    private void fixIntent(Intent intent) {
+        // This should be cleaned up: the call key used to send an Intent
+        // that just said to go to the recent calls list.  It now sends this
+        // abstract action, but this class hasn't been rewritten to deal with it.
+        if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
+            intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
+            intent.putExtra("call_key", true);
+            setIntent(intent);
+        }
+    }
+    
+    private void setupCallLogTab() {
+        mTabHost.addTab(mTabHost.newTabSpec("call_log")
+                .setIndicator(getString(R.string.recentCallsIconLabel),
+                        getResources().getDrawable(R.drawable.ic_tab_recent))
+                .setContent(new Intent("com.android.phone.action.RECENT_CALLS")));
+    }
+
+    private void setupDialerTab() {
+        mTabHost.addTab(mTabHost.newTabSpec("dialer")
+                .setIndicator(getString(R.string.dialerIconLabel),
+                        getResources().getDrawable(R.drawable.ic_tab_dialer))
+                .setContent(new Intent("com.android.phone.action.TOUCH_DIALER")));
+    }
+
+    private void setupContactsTab() {
+        mTabHost.addTab(mTabHost.newTabSpec("contacts")
+                .setIndicator(getText(R.string.contactsIconLabel),
+                        getResources().getDrawable(R.drawable.ic_tab_contacts))
+                .setContent(new Intent(UI.LIST_DEFAULT)));
+    }
+
+    private void setupFavoritesTab() {
+        Intent tab2Intent;
+        switch (FAVORITES_TAB_MODE) {
+            case FAVORITES_STARRED:
+                tab2Intent = new Intent(UI.LIST_STARRED_ACTION);
+                break;
+
+            case FAVORITES_FREQUENT:
+                tab2Intent = new Intent(UI.LIST_FREQUENT_ACTION);
+                break;
+
+            case FAVORITES_STREQUENT:
+                tab2Intent = new Intent(UI.LIST_STREQUENT_ACTION);
+                break;
+
+            default:
+                throw new UnsupportedOperationException("unknown default mode");
+        }
+        Drawable tab2Icon = getResources().getDrawable(R.drawable.ic_tab_starred);
+
+        mTabHost.addTab(mTabHost.newTabSpec("favorites")
+                .setIndicator(getString(R.string.contactsFavoritesLabel), tab2Icon)
+                .setContent(tab2Intent));
+    }
+
+    /**
+     * Returns true if the intent is due to hitting the green send key while in a call.
+     * 
+     * @param intent the intent that launched this activity
+     * @param recentCallsRequest true if the intent is requesting to view recent calls
+     * @return true if the intent is due to hitting the green send key while in a call 
+     */
+    private boolean isSendKeyWhileInCall(final Intent intent, final boolean recentCallsRequest) {
+        // If there is a call in progress go to the call screen
+        if (recentCallsRequest) {
+            final boolean callKey = intent.getBooleanExtra("call_key", false);
+
+            try {
+                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+                if (callKey && phone != null && phone.showCallScreen()) {
+                    return true;
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to handle send while in call", e);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Sets the current tab based on the intent's request type
+     * 
+     * @param recentCallsRequest true is the recent calls tab is desired, false oltherwise
+     */
+    private void setCurrentTab(Intent intent) {
+        final boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.getType());
+        if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
+            finish();
+            return;
+        }
+        intent.putExtra(EXTRA_IGNORE_STATE, true);
+        if (intent.getComponent().getClassName().equals(getClass().getName())) {
+            if (recentCallsRequest) {
+                mTabHost.setCurrentTab(1);
+            } else {
+                mTabHost.setCurrentTab(0);
+            }
+        } else {
+            mTabHost.setCurrentTab(2);
+        }
+        intent.putExtra(EXTRA_IGNORE_STATE, false);
+    }
+
+    @Override
+    public void onNewIntent(Intent newIntent) {
+        setIntent(newIntent);
+        fixIntent(newIntent);
+        setCurrentTab(newIntent);
+        final String action = newIntent.getAction();
+        if (action.equals(Contacts.Intents.UI.FILTER_CONTACTS_ACTION)) {
+            setupFilterText(newIntent);
+        } else if (isDialIntent(newIntent)) {
+            setupDialUri(newIntent);
+        }
+    }
+    
+    private boolean isDialIntent(Intent intent) {
+        final String action = intent.getAction();
+        if (Intent.ACTION_DIAL.equals(action)) {
+            return true;
+        }
+        if (Intent.ACTION_VIEW.equals(action)) {
+            final Uri data = intent.getData();
+            if (data != null && "tel".equals(data.getScheme())) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Retrieves the filter text stored in {@link #setupFilterText(Intent)}.
+     * This text originally came from a FILTER_CONTACTS_ACTION intent received
+     * by this activity. The stored text will then be cleared after after this
+     * method returns.
+     * 
+     * @return The stored filter text
+     */
+    public String getAndClearFilterText() {
+        String filterText = mFilterText;
+        mFilterText = null;
+        return filterText;
+    }
+
+    /**
+     * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent.
+     * This is so child activities can check if they are supposed to display a filter.
+     * 
+     * @param intent The intent received in {@link #onNewIntent(Intent)}
+     */
+    private void setupFilterText(Intent intent) {
+        // If the intent was relaunched from history, don't apply the filter text.
+        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
+            return;
+        }
+        String filter = intent.getStringExtra(Contacts.Intents.UI.FILTER_TEXT_EXTRA_KEY);
+        if (filter != null && filter.length() > 0) {
+            mFilterText = filter;
+        }
+    }
+
+    /**
+     * Retrieves the uri stored in {@link #setupDialUri(Intent)}. This uri
+     * originally came from a dial intent received by this activity. The stored
+     * uri will then be cleared after after this method returns.
+     * 
+     * @return The stored uri
+     */
+    public Uri getAndClearDialUri() {
+        Uri dialUri = mDialUri;
+        mDialUri = null;
+        return dialUri;
+    }
+
+    /**
+     * Stores the uri associated with a dial intent. This is so child activities can
+     * check if they are supposed to display new dial info.
+     * 
+     * @param intent The intent received in {@link #onNewIntent(Intent)}
+     */
+    private void setupDialUri(Intent intent) {
+        // If the intent was relaunched from history, don't reapply the intent.
+        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
+            return;
+        }
+        mDialUri = intent.getData();
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        // Handle BACK
+        if (keyCode == KeyEvent.KEYCODE_BACK && isTaskRoot()) {
+            // Instead of stopping, simply push this to the back of the stack.
+            // This is only done when running at the top of the stack;
+            // otherwise, we have been launched by someone else so need to
+            // allow the user to go back to the caller.
+            moveTaskToBack(false);
+            return true;
+        }
+        
+        return super.onKeyDown(keyCode, event);
+    }
+}
diff --git a/src/com/android/contacts/EditContactActivity.java b/src/com/android/contacts/EditContactActivity.java
new file mode 100644
index 0000000..c301473
--- /dev/null
+++ b/src/com/android/contacts/EditContactActivity.java
@@ -0,0 +1,1976 @@
+/*
+ * Copyright (C) 2007 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.android.contacts;
+
+import com.google.android.collect.Lists;
+
+import static com.android.contacts.ContactEntryAdapter.CONTACT_CUSTOM_RINGTONE_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.CONTACT_NAME_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.CONTACT_NOTES_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.CONTACT_PROJECTION;
+import static com.android.contacts.ContactEntryAdapter.CONTACT_SEND_TO_VOICEMAIL_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_AUX_DATA_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_DATA_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_ID_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_ISPRIMARY_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_KIND_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_LABEL_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_PROJECTION;
+import static com.android.contacts.ContactEntryAdapter.METHODS_TYPE_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_COMPANY_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_ID_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_ISPRIMARY_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_LABEL_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_PROJECTION;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_TITLE_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_TYPE_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.PHONES_ID_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.PHONES_ISPRIMARY_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.PHONES_LABEL_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.PHONES_NUMBER_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.PHONES_PROJECTION;
+import static com.android.contacts.ContactEntryAdapter.PHONES_TYPE_COLUMN;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.preference.PreferenceManager;
+import android.provider.Contacts;
+import android.provider.Contacts.ContactMethods;
+import android.provider.Contacts.Intents.Insert;
+import android.provider.Contacts.Organizations;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.telephony.PhoneNumberFormattingTextWatcher;
+import android.text.TextUtils;
+import android.text.method.DialerKeyListener;
+import android.text.method.TextKeyListener;
+import android.text.method.TextKeyListener.Capitalize;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ExpandableListView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.SimpleExpandableListAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Activity for editing or inserting a contact. Note that if the contact data changes in the
+ * background while this activity is running, the updates will be overwritten.
+ */
+public final class EditContactActivity extends Activity implements View.OnClickListener,
+        ExpandableListView.OnChildClickListener {
+    private static final String TAG = "EditContactActivity";
+
+    private static final int STATE_UNKNOWN = 0;
+    /** Editing an existing contact */
+    private static final int STATE_EDIT = 1;
+    /** The full insert mode */
+    private static final int STATE_INSERT = 2;
+
+    /** The launch code when picking a photo and the raw data is returned */
+    private static final int PHOTO_PICKED_WITH_DATA = 3021;
+
+    /** The launch code when picking a ringtone */
+    private static final int RINGTONE_PICKED = 3023;
+    
+    // Label picker position info
+    final static int LABEL_PICKER_PHONES_POSITION = 0;
+    final static int LABEL_PICKER_EMAIL_POSITION = 1;
+    final static int LABEL_PICKER_IM_POSITION = 2;
+    final static int LABEL_PICKER_POSTAL_POSITION = 3;
+    final static int LABEL_PICKER_OTHER_POSITION = 4;
+
+    // These correspond to the string array in resources for picker "other" items
+    final static int OTHER_ORGANIZATION = 0;
+    final static int OTHER_NOTE = 1;
+    
+    // Dialog IDs
+    final static int LABEL_PICKER_ALL_TYPES_DIALOG = 1;
+    final static int DELETE_CONFIRMATION_DIALOG = 2;
+
+    // Menu item IDs
+    public static final int MENU_ITEM_SAVE = 1;
+    public static final int MENU_ITEM_DONT_SAVE = 2;
+    public static final int MENU_ITEM_DELETE = 3;
+    public static final int MENU_ITEM_ADD = 5;
+    public static final int MENU_ITEM_PHOTO = 6;
+    
+    // Key listener types
+    final static int INPUT_TEXT = 1;
+    final static int INPUT_TEXT_WORDS = 2;
+    final static int INPUT_TEXT_SENTENCES = 3;
+    final static int INPUT_DIALER = 4;
+
+    /** Used to represent an invalid type for a contact entry */
+    private static final int INVALID_TYPE = -1;
+    
+    /** The default type for a phone that is added via an intent */
+    private static final int DEFAULT_PHONE_TYPE = Phones.TYPE_MOBILE;
+
+    /** The default type for an email that is added via an intent */
+    private static final int DEFAULT_EMAIL_TYPE = ContactMethods.TYPE_HOME;
+
+    /** The default type for a postal address that is added via an intent */
+    private static final int DEFAULT_POSTAL_TYPE = ContactMethods.TYPE_HOME;
+
+    private int mState; // saved across instances
+    private boolean mInsert; // saved across instances
+    private Uri mUri; // saved across instances
+    /** In insert mode this is the photo */
+    private Bitmap mPhoto; // saved across instances
+    private boolean mPhotoChanged = false; // saved across instances
+    
+    private EditText mNameView;
+    private ImageView mPhotoImageView;
+    private Button mPhotoButton;
+    private CheckBox mSendToVoicemailCheckBox;
+    private LinearLayout mLayout;
+    private LayoutInflater mInflater;
+    private MenuItem mPhotoMenuItem;
+    private boolean mPhotoPresent = false;
+
+    // These are accessed by inner classes. They're package scoped to make access more efficient.
+    /* package */ ContentResolver mResolver;
+    /* package */ ArrayList<EditEntry> mPhoneEntries = new ArrayList<EditEntry>();
+    /* package */ ArrayList<EditEntry> mEmailEntries = new ArrayList<EditEntry>();
+    /* package */ ArrayList<EditEntry> mImEntries = new ArrayList<EditEntry>();
+    /* package */ ArrayList<EditEntry> mPostalEntries = new ArrayList<EditEntry>();
+    /* package */ ArrayList<EditEntry> mOtherEntries = new ArrayList<EditEntry>();
+    /* package */ ArrayList<ArrayList<EditEntry>> mSections = new ArrayList<ArrayList<EditEntry>>();
+
+    /* package */ static final int MSG_DELETE = 1;
+    /* package */ static final int MSG_CHANGE_LABEL = 2;
+    /* package */ static final int MSG_ADD_PHONE = 3;
+    /* package */ static final int MSG_ADD_EMAIL = 4;
+    /* package */ static final int MSG_ADD_POSTAL = 5;
+
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.photoButton:
+            case R.id.photoImage: {
+                doPickPhotoAction();
+                break;
+            }
+
+            case R.id.addMore:
+                doAddAction();
+                break;
+
+            case R.id.saveButton:
+                doSaveAction();
+                break;
+
+            case R.id.discardButton:
+                doRevertAction();
+                break;
+
+            case R.id.delete:
+            case R.id.delete2: {
+                EditEntry entry = findEntryForView(v);
+                if (entry != null) {
+                    // Clear the text and hide the view so it gets saved properly
+                    ((TextView) entry.view.findViewById(R.id.data)).setText(null);
+                    entry.view.setVisibility(View.GONE);
+                    entry.isDeleted = true;
+                }
+                break;
+            }
+
+            case R.id.label: {
+                EditEntry entry = findEntryForView(v);
+                if (entry != null) {
+                    String[] labels = getLabelsForKind(this, entry.kind);
+                    LabelPickedListener listener = new LabelPickedListener(entry, labels);
+                    new AlertDialog.Builder(EditContactActivity.this)
+                            .setItems(labels, listener)
+                            .setTitle(R.string.selectLabel)
+                            .show();
+                }
+                break;
+            }
+                
+            case R.id.data: {
+                EditEntry entry = findEntryForView(v);
+                if (isRingtoneEntry(entry)) {
+                    doPickRingtone(entry);
+                }
+                break;
+            }
+        }
+    }
+
+    private void setPhotoPresent(boolean present) {
+        mPhotoImageView.setVisibility(present ? View.VISIBLE : View.GONE);
+        mPhotoButton.setVisibility(present ? View.GONE : View.VISIBLE);
+        mPhotoPresent = present;
+        if (mPhotoMenuItem != null) {
+            if (present) {
+                mPhotoMenuItem.setTitle(R.string.removePicture);
+                mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_delete);
+            } else {
+                mPhotoMenuItem.setTitle(R.string.addPicture);
+                mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_add);
+            }
+        }
+    }
+    
+    private EditEntry findEntryForView(View v) {
+        // Try to find the entry for this view
+        EditEntry entry = null;
+        do {
+            Object tag = v.getTag();
+            if (tag != null && tag instanceof EditEntry) {
+                entry = (EditEntry) tag;
+                break;
+            } else {
+                ViewParent parent = v.getParent();
+                if (parent != null && parent instanceof View) {
+                    v = (View) parent;
+                } else {
+                    v = null;
+                }
+            }
+        } while (v != null);
+        return entry;
+    }
+
+    private DialogInterface.OnClickListener mDeleteContactDialogListener =
+            new DialogInterface.OnClickListener() {
+        public void onClick(DialogInterface dialog, int button) {
+            mResolver.delete(mUri, null, null);
+            finish();
+        }
+    };
+
+    private boolean mMobilePhoneAdded = false;
+    private boolean mPrimaryEmailAdded = false;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mResolver = getContentResolver();
+
+        // Build the list of sections
+        setupSections();
+
+        // Load the UI
+        setContentView(R.layout.edit_contact);
+        mLayout = (LinearLayout) findViewById(R.id.list);
+        mNameView = (EditText) findViewById(R.id.name);
+        mPhotoImageView = (ImageView) findViewById(R.id.photoImage);
+        mPhotoImageView.setOnClickListener(this);
+        mPhotoImageView.setVisibility(View.GONE);
+        mPhotoButton = (Button) findViewById(R.id.photoButton);
+        mPhotoButton.setOnClickListener(this);
+        mSendToVoicemailCheckBox = (CheckBox) findViewById(R.id.send_to_voicemail);
+
+        // Setup the bottom buttons 
+        View view = findViewById(R.id.addMore);
+        view.setOnClickListener(this);
+        view = findViewById(R.id.saveButton);
+        view.setOnClickListener(this);
+        view = findViewById(R.id.discardButton);
+        view.setOnClickListener(this);
+
+        mInflater = getLayoutInflater();
+
+        // Resolve the intent
+        mState = STATE_UNKNOWN;
+        Intent intent = getIntent();
+        String action = intent.getAction();
+        mUri = intent.getData();
+        if (mUri != null) {
+            if (action.equals(Intent.ACTION_EDIT)) {
+                if (icicle == null) {
+                    // Build the entries & views
+                    buildEntriesForEdit(getIntent().getExtras());
+                    buildViews();
+                }
+                mState = STATE_EDIT;
+            } else if (action.equals(Intent.ACTION_INSERT)) {
+                if (icicle == null) {
+                    // Build the entries & views
+                    buildEntriesForInsert(getIntent().getExtras());
+                    buildViews();
+                }
+                mState = STATE_INSERT;
+                mInsert = true;
+            }
+        }
+
+        if (mState == STATE_UNKNOWN) {
+            Log.e(TAG, "Cannot resolve intent: " + intent);
+            finish();
+            return;
+        }
+
+        if (mState == STATE_EDIT) {
+            setTitle(getResources().getText(R.string.editContact_title_edit));
+        } else {
+            setTitle(getResources().getText(R.string.editContact_title_insert));
+        }
+    }
+
+    private void setupSections() {
+        mSections.add(mPhoneEntries);
+        mSections.add(mEmailEntries);
+        mSections.add(mImEntries);
+        mSections.add(mPostalEntries);
+        mSections.add(mOtherEntries);
+    }
+    
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        outState.putParcelableArrayList("phoneEntries", mPhoneEntries);
+        outState.putParcelableArrayList("emailEntries", mEmailEntries);
+        outState.putParcelableArrayList("imEntries", mImEntries);
+        outState.putParcelableArrayList("postalEntries", mPostalEntries);
+        outState.putParcelableArrayList("otherEntries", mOtherEntries);
+        outState.putInt("state", mState);
+        outState.putBoolean("insert", mInsert);
+        outState.putParcelable("uri", mUri);
+        outState.putString("name", mNameView.getText().toString());
+        outState.putParcelable("photo", mPhoto);
+        outState.putBoolean("photoChanged", mPhotoChanged);
+        outState.putBoolean("sendToVoicemail", mSendToVoicemailCheckBox.isChecked());
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle inState) {
+        mPhoneEntries = inState.getParcelableArrayList("phoneEntries");
+        mEmailEntries = inState.getParcelableArrayList("emailEntries");
+        mImEntries = inState.getParcelableArrayList("imEntries");
+        mPostalEntries = inState.getParcelableArrayList("postalEntries");
+        mOtherEntries = inState.getParcelableArrayList("otherEntries");
+        setupSections();
+
+        mState = inState.getInt("state");
+        mInsert = inState.getBoolean("insert");
+        mUri = inState.getParcelable("uri");
+        mNameView.setText(inState.getString("name"));
+        mPhoto = inState.getParcelable("photo");
+        if (mPhoto != null) {
+            mPhotoImageView.setImageBitmap(mPhoto);
+            setPhotoPresent(true);
+        } else {
+            mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
+            setPhotoPresent(false);
+        }
+        mPhotoChanged = inState.getBoolean("photoChanged");
+        mSendToVoicemailCheckBox.setChecked(inState.getBoolean("sendToVoicemail"));
+        
+        // Now that everything is restored, build the view
+        buildViews();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (resultCode != RESULT_OK) {
+            return;
+        }
+
+        switch (requestCode) {
+            case PHOTO_PICKED_WITH_DATA: {
+                final Bundle extras = data.getExtras();
+                if (extras != null) {
+                    Bitmap photo = extras.getParcelable("data");
+                    mPhoto = photo;
+                    mPhotoChanged = true;
+                    mPhotoImageView.setImageBitmap(photo);
+                    setPhotoPresent(true);
+                }
+                break;
+            }
+
+            case RINGTONE_PICKED: {
+                Uri pickedUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
+                handleRingtonePicked(pickedUri);
+                break;
+            }
+        }
+    }
+    
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_BACK: {
+                doSaveAction();
+                return true;
+            }
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        menu.add(0, MENU_ITEM_SAVE, 0, R.string.menu_done)
+                .setIcon(android.R.drawable.ic_menu_save)
+                .setAlphabeticShortcut('\n');
+        menu.add(0, MENU_ITEM_DONT_SAVE, 0, R.string.menu_doNotSave)
+                .setIcon(android.R.drawable.ic_menu_close_clear_cancel)
+                .setAlphabeticShortcut('q');
+        if (!mInsert) {
+            menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
+                    .setIcon(android.R.drawable.ic_menu_delete);
+        }
+
+        menu.add(0, MENU_ITEM_ADD, 0, R.string.menu_addItem)
+                .setIcon(android.R.drawable.ic_menu_add)
+                .setAlphabeticShortcut('n');
+
+        mPhotoMenuItem = menu.add(0, MENU_ITEM_PHOTO, 0, null);
+        // Updates the state of the menu item
+        setPhotoPresent(mPhotoPresent);
+
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_ITEM_SAVE:
+                doSaveAction();
+                return true;
+    
+            case MENU_ITEM_DONT_SAVE:
+                doRevertAction();
+                return true;
+    
+            case MENU_ITEM_DELETE:
+                // Get confirmation
+                showDialog(DELETE_CONFIRMATION_DIALOG);
+                return true;
+    
+            case MENU_ITEM_ADD:
+                doAddAction();
+                return true;
+
+            case MENU_ITEM_PHOTO:
+                if (!mPhotoPresent) {
+                    doPickPhotoAction();
+                } else {
+                    doRemovePhotoAction();
+                }
+                return true;
+        }
+
+        return false;
+    }
+
+    private void doAddAction() {
+        showDialog(LABEL_PICKER_ALL_TYPES_DIALOG);
+    }
+
+    private void doRevertAction() {
+        finish();
+    }
+
+    private void doPickPhotoAction() {
+        Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
+        // TODO: get these values from constants somewhere
+        intent.setType("image/*");
+        intent.putExtra("crop", "true");
+        intent.putExtra("aspectX", 1);
+        intent.putExtra("aspectY", 1);
+        intent.putExtra("outputX", 96);
+        intent.putExtra("outputY", 96);
+        try {
+            intent.putExtra("return-data", true);
+            startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
+        } catch (ActivityNotFoundException e) {
+            new AlertDialog.Builder(EditContactActivity.this)
+                .setTitle(R.string.errorDialogTitle)
+                .setMessage(R.string.photoPickerNotFoundText)
+                .setPositiveButton(R.string.okButtonText, null)
+                .show();
+        }
+    }
+
+    private void doRemovePhotoAction() {
+        mPhoto = null;
+        mPhotoChanged = true;
+        mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
+        setPhotoPresent(false);
+    }
+    
+    private void doPickRingtone(EditEntry entry) {
+        Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+        // Allow user to pick 'Default'
+        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+        // Show only ringtones
+        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE);
+        // Don't show 'Silent'
+        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
+        if (entry.data != null) {
+            Uri ringtoneUri = Uri.parse(entry.data);
+            // Put checkmark next to the current ringtone for this contact
+            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, ringtoneUri);
+        }
+        // Launch!
+        startActivityForResult(intent, RINGTONE_PICKED);
+    }
+    
+    private void handleRingtonePicked(Uri pickedUri) {
+        EditEntry entry = getRingtoneEntry();
+        if (entry == null) {
+            Log.w(TAG, "Ringtone picked but could not find ringtone entry");
+            return;
+        }
+        
+        if (pickedUri == null || RingtoneManager.isDefault(pickedUri)) {
+            entry.data = null;
+        } else {
+            entry.data = pickedUri.toString();
+        }
+        
+        updateRingtoneView(entry);
+    }
+
+    private void updateRingtoneView(EditEntry entry) {
+        if (entry.data == null) {
+            updateDataView(entry, getString(R.string.default_ringtone));
+        } else {
+            Uri ringtoneUri = Uri.parse(entry.data);
+            Ringtone ringtone = RingtoneManager.getRingtone(this, ringtoneUri);
+            if (ringtone == null) {
+                Log.w(TAG, "ringtone's URI doesn't resolve to a Ringtone");
+                return;
+            }
+            updateDataView(entry, ringtone.getTitle(this));
+        }
+    }
+    
+    private void updateDataView(EditEntry entry, String text) {
+        TextView dataView = (TextView) entry.view.findViewById(R.id.data);
+        dataView.setText(text);
+    }
+    
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        switch (id) {
+            case LABEL_PICKER_ALL_TYPES_DIALOG:
+                return createAllTypesPicker();
+
+            case DELETE_CONFIRMATION_DIALOG:
+                return new AlertDialog.Builder(EditContactActivity.this)
+                        .setTitle(R.string.deleteConfirmation_title)
+                        .setIcon(android.R.drawable.ic_dialog_alert)
+                        .setMessage(R.string.deleteConfirmation)
+                        .setNegativeButton(R.string.noButton, null)
+                        .setPositiveButton(R.string.yesButton, mDeleteContactDialogListener)
+                        .setCancelable(false)
+                        .create();
+        }
+        return super.onCreateDialog(id);
+    }
+    
+    static String[] getLabelsForKind(Context context, int kind) {
+        final Resources resources = context.getResources();
+        switch (kind) {
+            case Contacts.KIND_PHONE:
+                return resources.getStringArray(android.R.array.phoneTypes);
+            case Contacts.KIND_EMAIL:
+                return resources.getStringArray(android.R.array.emailAddressTypes);
+            case Contacts.KIND_POSTAL:
+                return resources.getStringArray(android.R.array.postalAddressTypes);
+            case Contacts.KIND_IM:
+                return resources.getStringArray(android.R.array.imProtocols);
+            case Contacts.KIND_ORGANIZATION:
+                return resources.getStringArray(android.R.array.organizationTypes);
+            case EditEntry.KIND_CONTACT:
+                return resources.getStringArray(R.array.otherLabels);
+        }
+        return null;
+    }
+
+    int getTypeFromLabelPosition(CharSequence[] labels, int labelPosition) {
+        // In the UI Custom... comes last, but it is uses the constant 0
+        // so it is in the same location across the various kinds. Fix up the
+        // position to a valid type here.
+        if (labelPosition == labels.length - 1) {
+            return ContactMethods.TYPE_CUSTOM;
+        } else {
+            return labelPosition + 1;
+        }
+    }
+
+    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
+            int childPosition, long id) {
+        EditEntry entry = null;
+
+        // Make the dialog go away
+        dismissDialog(LABEL_PICKER_ALL_TYPES_DIALOG);
+
+        // Create the new entry
+        switch (groupPosition) {
+            case LABEL_PICKER_PHONES_POSITION: {
+                String[] labels = getLabelsForKind(this, Contacts.KIND_PHONE);
+                final int type = getTypeFromLabelPosition(labels, childPosition);
+                entry = EditEntry.newPhoneEntry(EditContactActivity.this,
+                        labels[childPosition], type,
+                        null, Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY), 0);
+                if (type == Phones.TYPE_CUSTOM) {
+                    createCustomPicker(entry, mPhoneEntries);
+                    return true;
+                } else {
+                    mPhoneEntries.add(entry);
+                }
+                break;
+            }
+
+            case LABEL_PICKER_EMAIL_POSITION: {
+                String[] labels = getLabelsForKind(this, Contacts.KIND_EMAIL);
+                final int type = getTypeFromLabelPosition(labels, childPosition);
+                entry = EditEntry.newEmailEntry(EditContactActivity.this,
+                        labels[childPosition], type, null,
+                        Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY), 0);
+                if (type == ContactMethods.TYPE_CUSTOM) {
+                    createCustomPicker(entry, mEmailEntries);
+                    return true;
+                } else {
+                    mEmailEntries.add(entry);
+                }
+                break;
+            }
+            
+            case LABEL_PICKER_IM_POSITION: {
+                String[] labels = getLabelsForKind(this, Contacts.KIND_IM);
+                entry = EditEntry.newImEntry(EditContactActivity.this,
+                        labels[childPosition], childPosition, null,
+                        Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY), 0);
+                mImEntries.add(entry);
+                break;
+            }
+
+            case LABEL_PICKER_POSTAL_POSITION: {
+                String[] labels = getLabelsForKind(this, Contacts.KIND_POSTAL);
+                final int type = getTypeFromLabelPosition(labels, childPosition);
+                entry = EditEntry.newPostalEntry(EditContactActivity.this,
+                        labels[childPosition], type, null,
+                        Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY), 0);
+                if (type == ContactMethods.TYPE_CUSTOM) {
+                    createCustomPicker(entry, mPostalEntries);
+                    return true;
+                } else {
+                    mPostalEntries.add(entry);
+                }
+                break;
+            }
+            
+            case LABEL_PICKER_OTHER_POSITION: {
+                switch (childPosition) {
+                    case OTHER_ORGANIZATION:
+                        entry = EditEntry.newOrganizationEntry(EditContactActivity.this,
+                                Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY),
+                                ContactMethods.TYPE_WORK);
+                        mOtherEntries.add(entry);
+                        break;
+
+                    case OTHER_NOTE:
+                        entry = EditEntry.newNotesEntry(EditContactActivity.this, null, mUri);
+                        mOtherEntries.add(entry);
+                        break;
+                        
+                    default:
+                        entry = null;
+                }
+                break;
+            }
+
+            default:
+                entry = null;
+        }
+
+        // Rebuild the views if needed
+        if (entry != null) {
+            buildViews();
+
+            View dataView = entry.view.findViewById(R.id.data);
+            if (dataView == null) {
+                entry.view.requestFocus();
+            } else {
+                dataView.requestFocus();
+            }
+        }
+        return true;
+    }
+
+    private EditEntry getRingtoneEntry() {
+        for (int i = mOtherEntries.size() - 1; i >= 0; i--) {
+            EditEntry entry = mOtherEntries.get(i);
+            if (isRingtoneEntry(entry)) {
+                return entry;
+            }
+        }
+        return null;
+    }
+    
+    private static boolean isRingtoneEntry(EditEntry entry) {
+        return entry != null && entry.column != null && entry.column.equals(People.CUSTOM_RINGTONE);
+    }
+    
+    private Dialog createAllTypesPicker() {
+        // Setup the adapter
+        List<Map<String, ?>> groupData = Lists.newArrayList();
+        List<List<Map<String, ?>>> childData = Lists.newArrayList();
+        List<Map<String, ?>> children;
+        HashMap<String, CharSequence> curGroupMap;
+        CharSequence[] labels;
+        int labelsSize;
+
+        // Phones
+        curGroupMap = new HashMap<String, CharSequence>();
+        groupData.add(curGroupMap);
+        curGroupMap.put("data", getText(R.string.phoneLabelsGroup));
+
+        labels = getLabelsForKind(this, Contacts.KIND_PHONE);
+        labelsSize = labels.length;
+        children = Lists.newArrayList();
+        for (int i = 0; i < labelsSize; i++) {
+            HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
+            children.add(curChildMap);
+            curChildMap.put("data", labels[i]);
+        }
+        childData.add(LABEL_PICKER_PHONES_POSITION, children);
+
+        // Email
+        curGroupMap = new HashMap<String, CharSequence>();
+        groupData.add(curGroupMap);
+        curGroupMap.put("data", getText(R.string.emailLabelsGroup));
+
+        labels = getLabelsForKind(this, Contacts.KIND_EMAIL);
+        labelsSize = labels.length;
+        children = Lists.newArrayList();
+        for (int i = 0; i < labelsSize; i++) {
+            HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
+            children.add(curChildMap);
+            curChildMap.put("data", labels[i]);
+        }
+        childData.add(LABEL_PICKER_EMAIL_POSITION, children);
+
+        // IM
+        curGroupMap = new HashMap<String, CharSequence>();
+        groupData.add(curGroupMap);
+        curGroupMap.put("data", getText(R.string.imLabelsGroup));
+
+        labels = getLabelsForKind(this, Contacts.KIND_IM);
+        labelsSize = labels.length;
+        children = Lists.newArrayList();
+        for (int i = 0; i < labelsSize; i++) {
+            HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
+            children.add(curChildMap);
+            curChildMap.put("data", labels[i]);
+        }
+        childData.add(LABEL_PICKER_IM_POSITION, children);
+        
+        // Postal
+        curGroupMap = new HashMap<String, CharSequence>();
+        groupData.add(curGroupMap);
+        curGroupMap.put("data", getText(R.string.postalLabelsGroup));
+
+        labels = getLabelsForKind(this, Contacts.KIND_POSTAL);
+        labelsSize = labels.length;
+        children = Lists.newArrayList();
+        for (int i = 0; i < labelsSize; i++) {
+            HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
+            children.add(curChildMap);
+            curChildMap.put("data", labels[i]);
+        }
+        childData.add(LABEL_PICKER_POSTAL_POSITION, children);
+
+        // Other
+        curGroupMap = new HashMap<String, CharSequence>();
+        groupData.add(curGroupMap);
+        curGroupMap.put("data", getText(R.string.otherLabelsGroup));
+
+        labels = getLabelsForKind(this, EditEntry.KIND_CONTACT);
+        labelsSize = labels.length;
+        children = Lists.newArrayList();
+        for (int i = 0; i < labelsSize; i++) {
+            HashMap<String, CharSequence> curChildMap = new HashMap<String, CharSequence>();
+            children.add(curChildMap);
+            curChildMap.put("data", labels[i]);
+        }
+        childData.add(LABEL_PICKER_OTHER_POSITION, children);
+
+        // Create the expandable list view
+        ExpandableListView list = new ExpandableListView(new ContextThemeWrapper(this,
+                android.R.style.Theme_Light));
+        list.setOnChildClickListener(this);
+        list.setAdapter(new SimpleExpandableListAdapter(
+                new ContextThemeWrapper(this, android.R.style.Theme_Light),
+                groupData,
+                android.R.layout.simple_expandable_list_item_1,
+                new String[] { "data" },
+                new int[] { android.R.id.text1 },
+                childData,
+                android.R.layout.simple_expandable_list_item_1,
+                new String[] { "data" },
+                new int[] { android.R.id.text1 }
+                ));
+        // This list shouldn't have a color hint since the dialog may be transparent
+        list.setCacheColorHint(0);
+
+        // Create the dialog
+        return new AlertDialog.Builder(this).setView(list).setInverseBackgroundForced(true)
+                .setTitle(R.string.selectLabel).create();
+    }
+
+    private void createCustomPicker(final EditEntry entry, final ArrayList<EditEntry> addTo) {
+        final EditText label = new EditText(this);
+        label.setKeyListener(TextKeyListener.getInstance(false, Capitalize.WORDS));
+        label.requestFocus();
+        new AlertDialog.Builder(this)
+                .setView(label)
+                .setTitle(R.string.customLabelPickerTitle)
+                .setPositiveButton(R.string.okButtonText, new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int which) {
+                        entry.setLabel(EditContactActivity.this, ContactMethods.TYPE_CUSTOM,
+                                label.getText().toString());
+                        if (addTo != null) {
+                            addTo.add(entry);
+                            buildViews();
+                            entry.view.requestFocus(View.FOCUS_DOWN);
+                        }
+                    }
+                })
+                .setNegativeButton(R.string.cancelButtonText, null)
+                .show();
+    }
+    
+    /**
+     * Saves or creates the contact based on the mode, and if sucessful finishes the activity.
+     */
+    private void doSaveAction() {
+        // Save or create the contact if needed
+        switch (mState) {
+            case STATE_EDIT:
+                save();
+                break;
+
+            case STATE_INSERT:
+                create();
+                break;
+
+            default:
+                Log.e(TAG, "Unknown state in doSaveOrCreate: " + mState);
+                break;
+        }
+        finish();
+    }
+    
+    /**
+     * Save the various fields to the existing contact.
+     */
+    private void save() {
+        ContentValues values = new ContentValues();
+        String data;
+        int numValues = 0;
+
+        // Handle the name and send to voicemail specially
+        final String name = mNameView.getText().toString();
+        if (name != null && TextUtils.isGraphic(name)) {
+            numValues++;
+        }
+        values.put(People.NAME, name);
+        values.put(People.SEND_TO_VOICEMAIL, mSendToVoicemailCheckBox.isChecked() ? 1 : 0);
+        mResolver.update(mUri, values, null, null);
+
+        if (mPhotoChanged) {
+            // Only write the photo if it's changed, since we don't initially load mPhoto
+            if (mPhoto != null) {
+                ByteArrayOutputStream stream = new ByteArrayOutputStream();
+                mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
+                Contacts.People.setPhotoData(mResolver, mUri, stream.toByteArray());
+            } else {
+                Contacts.People.setPhotoData(mResolver, mUri, null);
+            }
+        }
+
+        int entryCount = ContactEntryAdapter.countEntries(mSections, false);
+        for (int i = 0; i < entryCount; i++) {
+            EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
+            int kind = entry.kind;
+            data = entry.getData();
+            boolean empty = data == null || !TextUtils.isGraphic(data);
+            if (kind == EditEntry.KIND_CONTACT) {
+                values.clear();
+                if (!empty) {
+                    values.put(entry.column, data);
+                    mResolver.update(entry.uri, values, null, null);
+                    numValues++;
+                } else {
+                    values.put(entry.column, (String) null);
+                    mResolver.update(entry.uri, values, null, null);
+                }
+            } else {
+                if (!empty) {
+                    values.clear();
+                    entry.toValues(values);
+                    if (entry.id != 0) {
+                        mResolver.update(entry.uri, values, null, null);
+                    } else {
+                        mResolver.insert(entry.uri, values);
+                    }
+                    numValues++;
+                } else if (entry.id != 0) {
+                    mResolver.delete(entry.uri, null, null);
+                }
+            }
+        }
+
+        if (numValues == 0) {
+            // The contact is completely empty, delete it
+            mResolver.delete(mUri, null, null);
+            mUri = null;
+            setResult(RESULT_CANCELED);
+        } else {
+            // Add the entry to the my contacts group if it isn't there already
+            People.addToMyContactsGroup(mResolver, ContentUris.parseId(mUri));
+            setResult(RESULT_OK, new Intent().setData(mUri));
+            Toast.makeText(this, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    /**
+     * Takes the entered data and saves it to a new contact.
+     */
+    private void create() {
+        ContentValues values = new ContentValues();
+        String data;
+        int numValues = 0;
+
+        // Create the contact itself
+        final String name = mNameView.getText().toString();
+        if (name != null && TextUtils.isGraphic(name)) {
+            numValues++;
+        }
+        values.put(People.NAME, name);
+        values.put(People.SEND_TO_VOICEMAIL, mSendToVoicemailCheckBox.isChecked() ? 1 : 0);
+
+        // Add the contact to the My Contacts group
+        Uri contactUri = People.createPersonInMyContactsGroup(mResolver, values);
+
+        // Add the contact to the group that is being displayed in the contact list
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+        int displayType = prefs.getInt(ContactsPreferenceActivity.PREF_DISPLAY_TYPE,
+                ContactsPreferenceActivity.DISPLAY_TYPE_UNKNOWN);
+        if (displayType == ContactsPreferenceActivity.DISPLAY_TYPE_USER_GROUP) {
+            String displayGroup = prefs.getString(ContactsPreferenceActivity.PREF_DISPLAY_INFO,
+                    null);
+            if (!TextUtils.isEmpty(displayGroup)) {
+                People.addToGroup(mResolver, ContentUris.parseId(contactUri), displayGroup);
+            }
+        }
+
+        // Handle the photo
+        if (mPhoto != null) {
+            ByteArrayOutputStream stream = new ByteArrayOutputStream();
+            mPhoto.compress(Bitmap.CompressFormat.JPEG, 75, stream);
+            Contacts.People.setPhotoData(getContentResolver(), contactUri, stream.toByteArray());
+        }
+
+        // Create the contact methods
+        int entryCount = ContactEntryAdapter.countEntries(mSections, false);
+        for (int i = 0; i < entryCount; i++) {
+            EditEntry entry = ContactEntryAdapter.getEntry(mSections, i, false);
+            if (entry.kind != EditEntry.KIND_CONTACT) {
+                values.clear();
+                if (entry.toValues(values)) {
+                    // Only create the entry if there is data
+                    entry.uri = mResolver.insert(
+                            Uri.withAppendedPath(contactUri, entry.contentDirectory), values);
+                    entry.id = ContentUris.parseId(entry.uri);
+                    numValues++;
+                }
+            } else {
+                // Update the contact with any straggling data, like notes
+                data = entry.getData();
+                values.clear();
+                if (data != null && TextUtils.isGraphic(data)) {
+                    values.put(entry.column, data);
+                    mResolver.update(contactUri, values, null, null);
+                    numValues++;
+                }
+            }
+        }
+
+        if (numValues == 0) {
+            mResolver.delete(contactUri, null, null);
+            setResult(RESULT_CANCELED);
+        } else {
+            mUri = contactUri;
+            Intent resultIntent = new Intent()
+                    .setData(mUri)
+                    .putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
+            setResult(RESULT_OK, resultIntent);
+            Toast.makeText(this, R.string.contactCreatedToast, Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    /**
+     * Build up the entries to display on the screen.
+     *
+     * @param extras the extras used to start this activity, may be null
+     */
+    private void buildEntriesForEdit(Bundle extras) {
+        Cursor personCursor = mResolver.query(mUri, CONTACT_PROJECTION, null, null, null);
+        if (personCursor == null) {
+            Log.e(TAG, "invalid contact uri: " + mUri);
+            finish();
+            return;
+        } else if (!personCursor.moveToFirst()) {
+            Log.e(TAG, "invalid contact uri: " + mUri);
+            finish();
+            personCursor.close();
+            return;
+        }
+
+        // Clear out the old entries
+        int numSections = mSections.size();
+        for (int i = 0; i < numSections; i++) {
+            mSections.get(i).clear();
+        }
+
+        EditEntry entry;
+
+        // Name
+        mNameView.setText(personCursor.getString(CONTACT_NAME_COLUMN));
+
+        // Photo
+        mPhoto = People.loadContactPhoto(this, mUri, 0, null);
+        if (mPhoto == null) {
+            setPhotoPresent(false);
+        } else {
+            setPhotoPresent(true);
+            mPhotoImageView.setImageBitmap(mPhoto);
+        }
+
+        // Send to voicemail
+        mSendToVoicemailCheckBox
+                .setChecked(personCursor.getInt(CONTACT_SEND_TO_VOICEMAIL_COLUMN) == 1);
+
+        // Organizations
+        Uri organizationsUri = Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY);
+        Cursor organizationsCursor = mResolver.query(organizationsUri, ORGANIZATIONS_PROJECTION,
+                null, null, null);
+
+        if (organizationsCursor != null) {
+            while (organizationsCursor.moveToNext()) {
+                int type = organizationsCursor.getInt(ORGANIZATIONS_TYPE_COLUMN);
+                String label = organizationsCursor.getString(ORGANIZATIONS_LABEL_COLUMN);
+                String company = organizationsCursor.getString(ORGANIZATIONS_COMPANY_COLUMN);
+                String title = organizationsCursor.getString(ORGANIZATIONS_TITLE_COLUMN);
+                long id = organizationsCursor.getLong(ORGANIZATIONS_ID_COLUMN);
+                Uri uri = ContentUris.withAppendedId(Organizations.CONTENT_URI, id);
+
+                // Add an organization entry
+                entry = EditEntry.newOrganizationEntry(this, label, type, company, title, uri, id);
+                entry.isPrimary = organizationsCursor.getLong(ORGANIZATIONS_ISPRIMARY_COLUMN) != 0;
+                mOtherEntries.add(entry);
+            }
+            organizationsCursor.close();
+        }
+
+        // Notes
+        if (!personCursor.isNull(CONTACT_NOTES_COLUMN)) {
+            entry = EditEntry.newNotesEntry(this, personCursor.getString(CONTACT_NOTES_COLUMN),
+                    mUri);
+            mOtherEntries.add(entry);
+        }
+
+        // Ringtone
+        entry = EditEntry.newRingtoneEntry(this,
+                personCursor.getString(CONTACT_CUSTOM_RINGTONE_COLUMN), mUri);
+        mOtherEntries.add(entry);
+        personCursor.close();
+
+        // Build up the phone entries
+        Uri phonesUri = Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY);
+        Cursor phonesCursor = mResolver.query(phonesUri, PHONES_PROJECTION,
+                null, null, null);
+
+        if (phonesCursor != null) {
+            while (phonesCursor.moveToNext()) {
+                int type = phonesCursor.getInt(PHONES_TYPE_COLUMN);
+                String label = phonesCursor.getString(PHONES_LABEL_COLUMN);
+                String number = phonesCursor.getString(PHONES_NUMBER_COLUMN);
+                long id = phonesCursor.getLong(PHONES_ID_COLUMN);
+                boolean isPrimary = phonesCursor.getLong(PHONES_ISPRIMARY_COLUMN) != 0;
+                Uri uri = ContentUris.withAppendedId(phonesUri, id);
+
+                // Add a phone number entry
+                entry = EditEntry.newPhoneEntry(this, label, type, number, uri, id);
+                entry.isPrimary = isPrimary;
+                mPhoneEntries.add(entry);
+
+                // Keep track of which primary types have been added
+                if (type == Phones.TYPE_MOBILE) {
+                    mMobilePhoneAdded = true;
+                }
+            }
+
+            phonesCursor.close();
+        }
+
+        // Build the contact method entries
+        Uri methodsUri = Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY);
+        Cursor methodsCursor = mResolver.query(methodsUri, METHODS_PROJECTION, null, null, null);
+
+        if (methodsCursor != null) {
+            while (methodsCursor.moveToNext()) {
+                int kind = methodsCursor.getInt(METHODS_KIND_COLUMN);
+                String label = methodsCursor.getString(METHODS_LABEL_COLUMN);
+                String data = methodsCursor.getString(METHODS_DATA_COLUMN);
+                String auxData = methodsCursor.getString(METHODS_AUX_DATA_COLUMN);
+                int type = methodsCursor.getInt(METHODS_TYPE_COLUMN);
+                long id = methodsCursor.getLong(METHODS_ID_COLUMN);
+                boolean isPrimary = methodsCursor.getLong(METHODS_ISPRIMARY_COLUMN) != 0;
+                Uri uri = ContentUris.withAppendedId(methodsUri, id);
+
+                switch (kind) {
+                    case Contacts.KIND_EMAIL: {
+                        entry = EditEntry.newEmailEntry(this, label, type, data, uri, id);
+                        entry.isPrimary = isPrimary;
+                        mEmailEntries.add(entry);
+    
+                        if (isPrimary) {
+                            mPrimaryEmailAdded = true;
+                        }
+                        break;
+                    }
+
+                    case Contacts.KIND_POSTAL: {
+                        entry = EditEntry.newPostalEntry(this, label, type, data, uri, id);
+                        entry.isPrimary = isPrimary;
+                        mPostalEntries.add(entry);
+                        break;
+                    }
+
+                    case Contacts.KIND_IM: {
+                        Object protocolObj = ContactMethods.decodeImProtocol(auxData);
+                        if (protocolObj == null) {
+                            // Invalid IM protocol, log it then ignore.
+                            Log.e(TAG, "Couldn't decode IM protocol: " + auxData);
+                            continue;
+                        } else {
+                            if (protocolObj instanceof Number) {
+                                int protocol = ((Number) protocolObj).intValue();
+                                entry = EditEntry.newImEntry(this,
+                                        getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol, 
+                                        data, uri, id);
+                            } else {
+                                entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, data,
+                                        uri, id);
+                            }
+                            mImEntries.add(entry);
+                        }
+                        break;
+                    }
+                }
+            }
+
+            methodsCursor.close();
+        }
+
+        // Add values from the extras, if there are any
+        if (extras != null) {
+            addFromExtras(extras, phonesUri, methodsUri);
+        }
+
+        // Add the base types if needed
+        if (!mMobilePhoneAdded) {
+            entry = EditEntry.newPhoneEntry(this,
+                    Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
+                    DEFAULT_PHONE_TYPE);
+            mPhoneEntries.add(entry);
+        }
+
+        if (!mPrimaryEmailAdded) {
+            entry = EditEntry.newEmailEntry(this,
+                    Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
+                    DEFAULT_EMAIL_TYPE);
+            entry.isPrimary = true;
+            mEmailEntries.add(entry);
+        }
+    }
+
+    /**
+     * Build the list of EditEntries for full mode insertions.
+     * 
+     * @param extras the extras used to start this activity, may be null
+     */
+    private void buildEntriesForInsert(Bundle extras) {
+        // Clear out the old entries
+        int numSections = mSections.size();
+        for (int i = 0; i < numSections; i++) {
+            mSections.get(i).clear();
+        }
+
+        EditEntry entry;
+
+        // Check the intent extras
+        if (extras != null) {
+            addFromExtras(extras, null, null);
+        }
+
+        // Photo
+        mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
+
+        // Add the base entries if they're not already present
+        if (!mMobilePhoneAdded) {
+            entry = EditEntry.newPhoneEntry(this, null, Phones.TYPE_MOBILE);
+            entry.isPrimary = true;
+            mPhoneEntries.add(entry);
+        }
+
+        if (!mPrimaryEmailAdded) {
+            entry = EditEntry.newEmailEntry(this, null, DEFAULT_EMAIL_TYPE);
+            entry.isPrimary = true;
+            mEmailEntries.add(entry);
+        }
+
+        // Ringtone
+        entry = EditEntry.newRingtoneEntry(this, null, mUri);
+        mOtherEntries.add(entry);
+        
+    }
+
+    private void addFromExtras(Bundle extras, Uri phonesUri, Uri methodsUri) {
+        EditEntry entry;
+
+        // Read the name from the bundle
+        CharSequence name = extras.getCharSequence(Insert.NAME);
+        if (name != null && TextUtils.isGraphic(name)) {
+            mNameView.setText(name);
+        }
+        
+        // Postal entries from extras
+        CharSequence postal = extras.getCharSequence(Insert.POSTAL);
+        int postalType = extras.getInt(Insert.POSTAL_TYPE, INVALID_TYPE);
+        if (!TextUtils.isEmpty(postal) && postalType == INVALID_TYPE) {
+            postalType = DEFAULT_POSTAL_TYPE;
+        }
+        
+        if (postalType != INVALID_TYPE) {
+            entry = EditEntry.newPostalEntry(this, null, postalType, postal.toString(),
+                    methodsUri, 0);
+            entry.isPrimary = extras.getBoolean(Insert.POSTAL_ISPRIMARY);
+            mPostalEntries.add(entry);
+        }
+
+        // Email entries from extras
+        CharSequence email = extras.getCharSequence(Insert.EMAIL);
+        int emailType = extras.getInt(Insert.EMAIL_TYPE, INVALID_TYPE);
+        if (!TextUtils.isEmpty(email) && emailType == INVALID_TYPE) {
+            emailType = DEFAULT_EMAIL_TYPE;
+            mPrimaryEmailAdded = true;
+        }
+   
+        if (emailType != INVALID_TYPE) {
+            entry = EditEntry.newEmailEntry(this, null, emailType, email.toString(), methodsUri, 0);
+            entry.isPrimary = extras.getBoolean(Insert.EMAIL_ISPRIMARY);
+            mEmailEntries.add(entry);
+
+            // Keep track of which primary types have been added
+            if (entry.isPrimary) {
+                mPrimaryEmailAdded = true;
+            }
+        }
+   
+        // Phone entries from extras 
+        CharSequence phoneNumber = extras.getCharSequence(Insert.PHONE);
+        int phoneType = extras.getInt(Insert.PHONE_TYPE, INVALID_TYPE);
+        if (!TextUtils.isEmpty(phoneNumber) && phoneType == INVALID_TYPE) {
+            phoneType = DEFAULT_PHONE_TYPE;
+        }
+   
+        if (phoneType != INVALID_TYPE) {
+            entry = EditEntry.newPhoneEntry(this, null, phoneType,
+                    phoneNumber.toString(), phonesUri, 0);
+            entry.isPrimary = extras.getBoolean(Insert.PHONE_ISPRIMARY);
+            mPhoneEntries.add(entry);
+
+            // Keep track of which primary types have been added
+            if (phoneType == Phones.TYPE_MOBILE) {
+                mMobilePhoneAdded = true;
+            }
+        }
+
+        // IM entries from extras
+        CharSequence imHandle = extras.getCharSequence(Insert.IM_HANDLE);
+        CharSequence imProtocol = extras.getCharSequence(Insert.IM_PROTOCOL);
+   
+        if (imHandle != null && imProtocol != null) {
+            Object protocolObj = ContactMethods.decodeImProtocol(imProtocol.toString());
+            if (protocolObj instanceof Number) {
+                int protocol = ((Number) protocolObj).intValue();
+                entry = EditEntry.newImEntry(this,
+                        getLabelsForKind(this, Contacts.KIND_IM)[protocol], protocol, 
+                        imHandle.toString(), null, 0);
+            } else {
+                entry = EditEntry.newImEntry(this, protocolObj.toString(), -1, imHandle.toString(),
+                        null, 0);
+            }
+            entry.isPrimary = extras.getBoolean(Insert.IM_ISPRIMARY);
+            mImEntries.add(entry);
+        }
+    }
+
+    /**
+     * Removes all existing views, builds new ones for all the entries, and adds them.
+     */
+    private void buildViews() {
+        // Remove existing views
+        final LinearLayout layout = mLayout;
+        layout.removeAllViews();
+
+        buildViewsForSection(layout, mPhoneEntries, R.string.listSeparatorCallNumber);
+        buildViewsForSection(layout, mEmailEntries, R.string.listSeparatorSendEmail);
+        buildViewsForSection(layout, mImEntries, R.string.listSeparatorSendIm);
+        buildViewsForSection(layout, mPostalEntries, R.string.listSeparatorMapAddress);
+        buildViewsForSection(layout, mOtherEntries, R.string.listSeparatorOtherInformation);
+    }
+
+
+    /**
+     * Builds the views for a specific section.
+     * 
+     * @param layout the container
+     * @param section the section to build the views for
+     */
+    private void buildViewsForSection(final LinearLayout layout, ArrayList<EditEntry> section,
+            int separatorResource) {
+        // Build the separator if the section isn't empty
+        if (section.size() > 0) {
+            View separator = mInflater.inflate(R.layout.edit_separator, layout, false);
+            TextView text = (TextView) separator.findViewById(R.id.text);
+            text.setText(getText(separatorResource));
+            layout.addView(separator);
+        }
+
+        // Build views for the current section
+        for (EditEntry entry : section) {
+            entry.activity = this; // this could be null from when the state is restored
+            if (!entry.isDeleted) {
+                View view = buildViewForEntry(entry);
+                layout.addView(view);
+            }
+        }
+    }
+
+    /**
+     * Builds a view to display an EditEntry.
+     * 
+     * @param entry the entry to display
+     * @return a view that will display the given entry
+     */
+    /* package */ View buildViewForEntry(final EditEntry entry) {
+        // Look for any existing entered text, and save it if found
+        if (entry.view != null && entry.syncDataWithView) {
+            String enteredText = ((TextView) entry.view.findViewById(R.id.data))
+                    .getText().toString();
+            if (!TextUtils.isEmpty(enteredText)) {
+                entry.data = enteredText;
+            }
+        }
+
+        // Build a new view
+        final ViewGroup parent = mLayout;
+        View view;
+
+        if (entry.kind == Contacts.KIND_ORGANIZATION) {
+            view = mInflater.inflate(R.layout.edit_contact_entry_org, parent, false);
+        } else if (isRingtoneEntry(entry)) {
+            view = mInflater.inflate(R.layout.edit_contact_entry_ringtone, parent, false);
+        } else if (!entry.isStaticLabel) {
+            view = mInflater.inflate(R.layout.edit_contact_entry, parent, false);
+        } else {
+            view = mInflater.inflate(R.layout.edit_contact_entry_static_label, parent, false);
+        }
+        entry.view = view;
+        
+        // Set the entry as the tag so we can find it again later given just the view
+        view.setTag(entry);
+
+        // Bind the label
+        entry.bindLabel(this);
+
+        // Bind data
+        TextView data = (TextView) view.findViewById(R.id.data);
+        TextView data2 = (TextView) view.findViewById(R.id.data2);
+
+        if (data instanceof Button) {
+            data.setOnClickListener(this);
+        }
+        if (data.length() == 0) {
+            if (entry.syncDataWithView) {
+                // If there is already data entered don't overwrite it
+                data.setText(entry.data);
+            } else {
+                fillViewData(entry);
+            }
+        }
+        if (data2 != null && data2.length() == 0) {
+            // If there is already data entered don't overwrite it
+            data2.setText(entry.data2);
+        }
+        data.setHint(entry.hint);
+        if (data2 != null) data2.setHint(entry.hint2);
+        if (entry.lines > 1) {
+            data.setLines(entry.lines);
+            data.setMaxLines(entry.maxLines);
+            if (data2 != null) {
+                data2.setLines(entry.lines);
+                data2.setMaxLines(entry.maxLines);
+            }
+        } else if (entry.lines >= 0) {
+            data.setSingleLine();
+            if (data2 != null) {
+                data2.setSingleLine();
+            }
+        }
+        switch (entry.keyListener) {
+            case INPUT_TEXT:
+                data.setKeyListener(TextKeyListener.getInstance());
+                if (data2 != null) {
+                    data2.setKeyListener(TextKeyListener.getInstance());
+                }
+                break;
+                
+            case INPUT_TEXT_WORDS:
+                data.setKeyListener(TextKeyListener.getInstance(true, Capitalize.WORDS));
+                if (data2 != null) {
+                    data2.setKeyListener(TextKeyListener.getInstance(true, Capitalize.WORDS));
+                }
+                break;
+                
+            case INPUT_TEXT_SENTENCES:
+                data.setKeyListener(TextKeyListener.getInstance(true, Capitalize.SENTENCES));
+                if (data2 != null) {
+                    data2.setKeyListener(TextKeyListener.getInstance(true, Capitalize.SENTENCES));
+                }
+                break;
+                
+            case INPUT_DIALER:
+                data.setKeyListener(DialerKeyListener.getInstance());
+                data.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
+                if (data2 != null) {
+                    data2.setKeyListener(DialerKeyListener.getInstance());
+                    data2.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
+                }
+                break;
+        }
+
+        // Hook up the delete button
+        View delete = view.findViewById(R.id.delete);
+        if (delete != null) delete.setOnClickListener(this);
+        View delete2 = view.findViewById(R.id.delete2);
+        if (delete2 != null) delete2.setOnClickListener(this);
+        
+        return view;
+    }
+
+    private void fillViewData(final EditEntry entry) {
+        if (isRingtoneEntry(entry)) {
+            updateRingtoneView(entry);
+        }
+    }
+    
+    /**
+     * Handles the results from the label change picker.
+     */
+    private final class LabelPickedListener implements DialogInterface.OnClickListener {
+        EditEntry mEntry;
+        String[] mLabels;
+
+        public LabelPickedListener(EditEntry entry, String[] labels) {
+            mEntry = entry;
+            mLabels = labels;
+        }
+
+        public void onClick(DialogInterface dialog, int which) {
+            // TODO: Use a managed dialog
+            if (mEntry.kind != Contacts.KIND_IM) {
+                final int type = getTypeFromLabelPosition(mLabels, which);
+                if (type == ContactMethods.TYPE_CUSTOM) {
+                    createCustomPicker(mEntry, null);
+                } else {
+                    mEntry.setLabel(EditContactActivity.this, type, mLabels[which]);
+                }
+            } else {
+                mEntry.setLabel(EditContactActivity.this, which, mLabels[which]);
+            }
+        }
+    }
+
+    /**
+     * A basic structure with the data for a contact entry in the list.
+     */
+    private static final class EditEntry extends ContactEntryAdapter.Entry implements Parcelable {
+        // These aren't stuffed into the parcel
+        public EditContactActivity activity;
+        public View view;
+
+        // These are stuffed into the parcel
+        public String hint;
+        public String hint2;
+        public String column;
+        public String contentDirectory;
+        public String data2;
+        public int keyListener;
+        public int type;
+        /**
+         * If 0 or 1, setSingleLine will be called. If negative, setSingleLine
+         * will not be called.
+         */
+        public int lines = 1;
+        public boolean isPrimary;
+        public boolean isDeleted = false;
+        public boolean isStaticLabel = false;
+        public boolean syncDataWithView = true;
+
+        private EditEntry() {
+            // only used by CREATOR
+        }
+
+        public EditEntry(EditContactActivity activity) {
+            this.activity = activity;
+        }
+
+        public EditEntry(EditContactActivity activity, String label,
+                int type, String data, Uri uri, long id) {
+            this.activity = activity;
+            this.isPrimary = false;
+            this.label = label;
+            this.type = type;
+            this.data = data;
+            this.uri = uri;
+            this.id = id;
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel parcel, int flags) {
+            // Make sure to read data from the input field, if anything is entered
+            data = getData();
+
+            // Write in our own fields.
+            parcel.writeString(hint);
+            parcel.writeString(hint2);
+            parcel.writeString(column);
+            parcel.writeString(contentDirectory);
+            parcel.writeString(data2);
+            parcel.writeInt(keyListener);
+            parcel.writeInt(type);
+            parcel.writeInt(lines);
+            parcel.writeInt(isPrimary ? 1 : 0);
+            parcel.writeInt(isDeleted ? 1 : 0);
+            parcel.writeInt(isStaticLabel ? 1 : 0);
+            parcel.writeInt(syncDataWithView ? 1 : 0);
+
+            // Write in the fields from Entry
+            super.writeToParcel(parcel);
+        }
+
+        public static final Parcelable.Creator<EditEntry> CREATOR =
+            new Parcelable.Creator<EditEntry>() {
+            public EditEntry createFromParcel(Parcel in) {
+                EditEntry entry = new EditEntry();
+
+                // Read out our own fields
+                entry.hint = in.readString();
+                entry.hint2 = in.readString();
+                entry.column = in.readString();
+                entry.contentDirectory = in.readString();
+                entry.data2 = in.readString();
+                entry.keyListener = in.readInt();
+                entry.type = in.readInt();
+                entry.lines = in.readInt();
+                entry.isPrimary = in.readInt() == 1;
+                entry.isDeleted = in.readInt() == 1;
+                entry.isStaticLabel = in.readInt() == 1;
+                entry.syncDataWithView = in.readInt() == 1;
+                
+                // Read out the fields from Entry
+                entry.readFromParcel(in);
+
+                return entry;
+            }
+            
+            public EditEntry[] newArray(int size) {
+                return new EditEntry[size];
+            }
+        };
+
+        public void setLabel(Context context, int typeIn, String labelIn) {
+            type = typeIn;
+            label = labelIn;
+            if (view != null) {
+                bindLabel(context);
+            }
+        }
+        
+        public void bindLabel(Context context) {
+            TextView v = (TextView) view.findViewById(R.id.label);
+            if (isStaticLabel) {
+                v.setText(label);
+                return;
+            }
+
+            switch (kind) {
+                case Contacts.KIND_PHONE: {
+                    v.setText(Phones.getDisplayLabel(context, type, label));
+                    break;
+                }
+
+                case Contacts.KIND_IM: {
+                    v.setText(getLabelsForKind(activity, kind)[type]);
+                    break;
+                }
+                
+                case Contacts.KIND_ORGANIZATION: {
+                    v.setText(Organizations.getDisplayLabel(activity, type, label));
+                    break;
+                }
+
+                default: {
+                    v.setText(Contacts.ContactMethods.getDisplayLabel(context, kind, type, label));
+                    if (kind == Contacts.KIND_POSTAL) {
+                        v.setMaxLines(3);
+                    }
+                    break;
+                }
+            }
+            v.setOnClickListener(activity);
+        }
+
+        /**
+         * Returns the data for the entry
+         * @return the data for the entry
+         */
+        public String getData() {
+            if (view != null && syncDataWithView) {
+                CharSequence text = ((TextView) view.findViewById(R.id.data)).getText();
+                if (text != null) {
+                    return text.toString();
+                }
+            }
+
+            if (data != null) {
+                return data.toString();
+            }
+
+            return null;
+        }
+
+        /**
+         * Dumps the entry into a HashMap suitable for passing to the database.
+         * 
+         * @param values the HashMap to fill in.
+         * @return true if the value should be saved, false otherwise
+         */
+        public boolean toValues(ContentValues values) {
+            boolean success = false;
+            String labelString = null;
+            // Save the type and label
+            if (view != null) {
+                // Read the possibly updated label from the text field
+                labelString = ((TextView) view.findViewById(R.id.label)).getText().toString();
+            }
+            switch (kind) {
+                case Contacts.KIND_PHONE:
+                    if (type != Phones.TYPE_CUSTOM) {
+                        labelString = null;
+                    }
+                    values.put(Phones.LABEL, labelString);
+                    values.put(Phones.TYPE, type);
+                    break;
+
+                case Contacts.KIND_EMAIL:
+                    if (type != ContactMethods.TYPE_CUSTOM) {
+                        labelString = null;
+                    }
+                    values.put(ContactMethods.LABEL, labelString);
+                    values.put(ContactMethods.KIND, kind);
+                    values.put(ContactMethods.TYPE, type);
+                    break;
+
+                case Contacts.KIND_IM:
+                    values.put(ContactMethods.KIND, kind);
+                    values.put(ContactMethods.TYPE, ContactMethods.TYPE_OTHER);
+                    values.putNull(ContactMethods.LABEL);
+                    if (type != -1) {
+                        values.put(ContactMethods.AUX_DATA,
+                                ContactMethods.encodePredefinedImProtocol(type));
+                    } else {
+                        values.put(ContactMethods.AUX_DATA,
+                                ContactMethods.encodeCustomImProtocol(label.toString()));
+                    }
+                    break;
+
+                case Contacts.KIND_POSTAL:
+                    if (type != ContactMethods.TYPE_CUSTOM) {
+                        labelString = null;
+                    }
+                    values.put(ContactMethods.LABEL, labelString);
+                    values.put(ContactMethods.KIND, kind);
+                    values.put(ContactMethods.TYPE, type);
+                    break;
+
+                case Contacts.KIND_ORGANIZATION:
+                    if (type != ContactMethods.TYPE_CUSTOM) {
+                        labelString = null;
+                    }
+                    values.put(ContactMethods.LABEL, labelString);
+                    values.put(ContactMethods.TYPE, type);
+                    // Save the title
+                    if (view != null) {
+                        // Read the possibly updated data from the text field
+                        data2 = ((TextView) view.findViewById(R.id.data2)).getText().toString();
+                    }
+                    if (!TextUtils.isGraphic(data2)) {
+                        values.putNull(Organizations.TITLE);
+                    } else {
+                        values.put(Organizations.TITLE, data2.toString());
+                        success = true;
+                    }
+                    break;
+
+                default:
+                    Log.w(TAG, "unknown kind " + kind);
+                    values.put(ContactMethods.LABEL, labelString);
+                    values.put(ContactMethods.KIND, kind);
+                    values.put(ContactMethods.TYPE, type);
+                    break;
+            }
+
+            values.put(ContactMethods.ISPRIMARY, isPrimary ? "1" : "0");
+
+            // Save the data
+            if (view != null && syncDataWithView) {
+                // Read the possibly updated data from the text field
+                data = ((TextView) view.findViewById(R.id.data)).getText().toString();
+            }
+            if (!TextUtils.isGraphic(data)) {
+                values.putNull(column);
+                return success;
+            } else {
+                values.put(column, data.toString());
+                return true;
+            }
+        }
+
+        /**
+         * Create a new empty organization entry
+         */
+        public static final EditEntry newOrganizationEntry(EditContactActivity activity,
+                Uri uri, int type) {
+            return newOrganizationEntry(activity, null, type, null, null, uri, 0);
+        }
+
+        /**
+         * Create a new company entry with the given data.
+         */
+        public static final EditEntry newOrganizationEntry(EditContactActivity activity,
+                String label, int type, String company, String title, Uri uri, long id) {
+            EditEntry entry = new EditEntry(activity, label, type, company, uri, id);
+            entry.hint = activity.getString(R.string.ghostData_company);
+            entry.hint2 = activity.getString(R.string.ghostData_title);
+            entry.data2 = title;
+            entry.column = Organizations.COMPANY;
+            entry.contentDirectory = Organizations.CONTENT_DIRECTORY;
+            entry.kind = Contacts.KIND_ORGANIZATION;
+            entry.keyListener = INPUT_TEXT_WORDS;
+            return entry;
+        }
+
+        /**
+         * Create a new notes entry with the given data.
+         */
+        public static final EditEntry newNotesEntry(EditContactActivity activity,
+                String data, Uri uri) {
+            EditEntry entry = new EditEntry(activity);
+            entry.label = activity.getString(R.string.label_notes);
+            entry.hint = activity.getString(R.string.ghostData_notes);
+            entry.data = data;
+            entry.uri = uri;
+            entry.column = People.NOTES;
+            entry.maxLines = 10;
+            entry.lines = 2;
+            entry.id = 0;
+            entry.kind = KIND_CONTACT;
+            entry.keyListener = INPUT_TEXT_SENTENCES;
+            entry.isStaticLabel = true;
+            return entry;
+        }
+
+        /**
+         * Create a new ringtone entry with the given data.
+         */
+        public static final EditEntry newRingtoneEntry(EditContactActivity activity,
+                String data, Uri uri) {
+            EditEntry entry = new EditEntry(activity);
+            entry.label = activity.getString(R.string.label_ringtone);
+            entry.data = data;
+            entry.uri = uri;
+            entry.column = People.CUSTOM_RINGTONE;
+            entry.kind = KIND_CONTACT;
+            entry.isStaticLabel = true;
+            entry.syncDataWithView = false;
+            entry.lines = -1;
+            return entry;
+        }
+
+        /**
+         * Create a new empty email entry
+         */
+        public static final EditEntry newPhoneEntry(EditContactActivity activity,
+                Uri uri, int type) {
+            return newPhoneEntry(activity, null, type, null, uri, 0);
+        }
+
+        /**
+         * Create a new phone entry with the given data.
+         */
+        public static final EditEntry newPhoneEntry(EditContactActivity activity,
+                String label, int type, String data, Uri uri,
+                long id) {
+            EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
+            entry.hint = activity.getString(R.string.ghostData_phone);
+            entry.column = People.Phones.NUMBER;
+            entry.contentDirectory = People.Phones.CONTENT_DIRECTORY;
+            entry.kind = Contacts.KIND_PHONE;
+            entry.keyListener = INPUT_DIALER;
+            return entry;
+        }
+
+        /**
+         * Create a new empty email entry
+         */
+        public static final EditEntry newEmailEntry(EditContactActivity activity,
+                Uri uri, int type) {
+            return newEmailEntry(activity, null, type, null, uri, 0);
+        }
+
+        /**
+         * Create a new email entry with the given data.
+         */
+        public static final EditEntry newEmailEntry(EditContactActivity activity,
+                String label, int type, String data, Uri uri,
+                long id) {
+            EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
+            entry.hint = activity.getString(R.string.ghostData_email);
+            entry.column = ContactMethods.DATA;
+            entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
+            entry.kind = Contacts.KIND_EMAIL;
+            entry.keyListener = INPUT_TEXT;
+            return entry;
+        }
+
+        /**
+         * Create a new empty postal address entry
+         */
+        public static final EditEntry newPostalEntry(EditContactActivity activity,
+                Uri uri, int type) {
+            return newPostalEntry(activity, null, type, null, uri, 0);
+        }
+
+        /**
+         * Create a new postal address entry with the given data.
+         *
+         * @param label label for the item, from the db not the display label
+         * @param type the type of postal address
+         * @param data the starting data for the entry, may be null
+         * @param uri the uri for the entry if it already exists, may be null
+         * @param id the id for the entry if it already exists, 0 it it doesn't
+         * @return the new EditEntry
+         */
+        public static final EditEntry newPostalEntry(EditContactActivity activity,
+                String label, int type, String data, Uri uri, long id) {
+            EditEntry entry = new EditEntry(activity, label, type, data, uri, id);
+            entry.hint = activity.getString(R.string.ghostData_postal);
+            entry.column = ContactMethods.DATA;
+            entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
+            entry.kind = Contacts.KIND_POSTAL;
+            entry.keyListener = INPUT_TEXT_WORDS;
+            entry.maxLines = 4;
+            entry.lines = 2;
+            return entry;
+        }
+
+        /**
+         * Create a new postal address entry with the given data.
+         *
+         * @param label label for the item, from the db not the display label
+         * @param protocol the type used
+         * @param data the starting data for the entry, may be null
+         * @param uri the uri for the entry if it already exists, may be null
+         * @param id the id for the entry if it already exists, 0 it it doesn't
+         * @return the new EditEntry
+         */
+        public static final EditEntry newImEntry(EditContactActivity activity,
+                String label, int protocol, String data, Uri uri, long id) {
+            EditEntry entry = new EditEntry(activity, label, protocol, data, uri, id);
+            entry.hint = activity.getString(R.string.ghostData_im);
+            entry.column = ContactMethods.DATA;
+            entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
+            entry.kind = Contacts.KIND_IM;
+            entry.keyListener = INPUT_TEXT;
+            return entry;
+        }
+    }
+}
diff --git a/src/com/android/contacts/FastScrollView.java b/src/com/android/contacts/FastScrollView.java
new file mode 100644
index 0000000..f45e947
--- /dev/null
+++ b/src/com/android/contacts/FastScrollView.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2008 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.android.contacts;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.OnHierarchyChangeListener;
+import android.widget.AbsListView;
+import android.widget.Adapter;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.HeaderViewListAdapter;
+import android.widget.ListView;
+import android.widget.AbsListView.OnScrollListener;
+
+/**
+ * FastScrollView is meant for embedding {@link ListView}s that contain a large number of 
+ * items that can be indexed in some fashion. It displays a special scroll bar that allows jumping
+ * quickly to indexed sections of the list in touch-mode. Only one child can be added to this 
+ * view group and it must be a {@link ListView}, with an adapter that is derived from 
+ * {@link BaseAdapter}.
+ */
+public class FastScrollView extends FrameLayout 
+        implements OnScrollListener, OnHierarchyChangeListener {
+
+    private Drawable mCurrentThumb;
+    private Drawable mOverlayDrawable;
+
+    private int mThumbH;
+    private int mThumbW;
+    private int mThumbY;
+
+    private RectF mOverlayPos;
+    
+    // Hard coding these for now
+    private int mOverlaySize = 104;
+
+    private boolean mDragging;
+    private ListView mList;
+    private boolean mScrollCompleted;
+    private boolean mThumbVisible;
+    private int mVisibleItem;
+    private Paint mPaint;
+    private int mListOffset;
+    
+    private Object [] mSections;
+    private String mSectionText;
+    private boolean mDrawOverlay;
+    private ScrollFade mScrollFade;
+    
+    private Handler mHandler = new Handler();
+    
+    private BaseAdapter mListAdapter;
+
+    private boolean mChangedBounds;
+
+    interface SectionIndexer {
+        Object[] getSections();
+        
+        int getPositionForSection(int section);
+        
+        int getSectionForPosition(int position);
+    }
+    
+    public FastScrollView(Context context) {
+        super(context);
+
+        init(context);
+    }
+
+
+    public FastScrollView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        
+        init(context);
+    }
+
+    public FastScrollView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        init(context);
+    }
+
+    private void useThumbDrawable(Drawable drawable) {
+        mCurrentThumb = drawable;
+        mThumbW = 64; //mCurrentThumb.getIntrinsicWidth();
+        mThumbH = 52; //mCurrentThumb.getIntrinsicHeight();
+        mChangedBounds = true;
+    }
+
+    private void init(Context context) {
+        // Get both the scrollbar states drawables
+        final Resources res = context.getResources();
+        useThumbDrawable(res.getDrawable(
+                com.android.internal.R.drawable.scrollbar_handle_accelerated_anim2));
+        
+        mOverlayDrawable = res.getDrawable(R.drawable.dialog_full_dark);
+        
+        mScrollCompleted = true;
+        setWillNotDraw(false);
+        
+        // Need to know when the ListView is added
+        setOnHierarchyChangeListener(this);
+        
+        mOverlayPos = new RectF();
+        mScrollFade = new ScrollFade();
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mPaint.setTextAlign(Paint.Align.CENTER);
+        mPaint.setTextSize(mOverlaySize / 2);
+        mPaint.setColor(0xFFFFFFFF);
+        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+    }
+    
+    private void removeThumb() {
+        
+        mThumbVisible = false;
+        // Draw one last time to remove thumb
+        invalidate();
+    }
+    
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        
+        if (!mThumbVisible) {
+            // No need to draw the rest
+            return;
+        }
+
+        final int y = mThumbY;
+        final int viewWidth = getWidth();
+        final FastScrollView.ScrollFade scrollFade = mScrollFade;
+
+        int alpha = -1;
+        if (scrollFade.mStarted) {
+            alpha = scrollFade.getAlpha();
+            if (alpha < ScrollFade.ALPHA_MAX / 2) {
+                mCurrentThumb.setAlpha(alpha * 2);
+            }
+            int left = viewWidth - (mThumbW * alpha) / ScrollFade.ALPHA_MAX;
+            mCurrentThumb.setBounds(left, 0, viewWidth, mThumbH);
+            mChangedBounds = true;
+        }
+
+        canvas.translate(0, y);
+        mCurrentThumb.draw(canvas);
+        canvas.translate(0, -y);
+
+        // If user is dragging the scroll bar, draw the alphabet overlay
+        if (mDragging && mDrawOverlay) {
+            mOverlayDrawable.draw(canvas);
+            final Paint paint = mPaint;
+            float descent = paint.descent();
+            final RectF rectF = mOverlayPos;
+            canvas.drawText(mSectionText, (int) (rectF.left + rectF.right) / 2,
+                    (int) (rectF.bottom + rectF.top) / 2 + mOverlaySize / 4 - descent, paint);
+        } else if (alpha == 0) {
+            scrollFade.mStarted = false;
+            removeThumb();
+        } else {
+            invalidate(viewWidth - mThumbW, y, viewWidth, y + mThumbH);            
+        }
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        if (mCurrentThumb != null) {
+            mCurrentThumb.setBounds(w - mThumbW, 0, w, mThumbH);
+        }
+        final RectF pos = mOverlayPos;
+        pos.left = (w - mOverlaySize) / 2;
+        pos.right = pos.left + mOverlaySize;
+        pos.top = h / 10; // 10% from top
+        pos.bottom = pos.top + mOverlaySize;
+        mOverlayDrawable.setBounds((int) pos.left, (int) pos.top,
+                (int) pos.right, (int) pos.bottom);
+    }
+    
+    public void onScrollStateChanged(AbsListView view, int scrollState) {
+    }
+    
+    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 
+            int totalItemCount) {
+        
+        if (totalItemCount - visibleItemCount > 0 && !mDragging) {
+            mThumbY = ((getHeight() - mThumbH) * firstVisibleItem) / (totalItemCount - visibleItemCount);
+            if (mChangedBounds) {
+                final int viewWidth = getWidth();
+                mCurrentThumb.setBounds(viewWidth - mThumbW, 0, viewWidth, mThumbH);
+                mChangedBounds = false;
+            }
+        }
+        mScrollCompleted = true;
+        if (firstVisibleItem == mVisibleItem) {
+            return;
+        }
+        mVisibleItem = firstVisibleItem;
+        if (!mThumbVisible || mScrollFade.mStarted) {
+            mThumbVisible = true;
+            mCurrentThumb.setAlpha(ScrollFade.ALPHA_MAX);
+        }
+        mHandler.removeCallbacks(mScrollFade);
+        mScrollFade.mStarted = false;
+        if (!mDragging) {
+            mHandler.postDelayed(mScrollFade, 1500);
+        }
+    }
+
+    
+    private void getSections() {
+        Adapter adapter = mList.getAdapter();
+        if (adapter instanceof HeaderViewListAdapter) {
+            mListOffset = ((HeaderViewListAdapter)adapter).getHeadersCount();
+            adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
+        }
+        if (adapter instanceof SectionIndexer) {
+            mListAdapter = (BaseAdapter) adapter;
+            mSections = ((SectionIndexer) mListAdapter).getSections();
+        }
+    }
+    
+    public void onChildViewAdded(View parent, View child) {
+        if (child instanceof ListView) {
+            mList = (ListView)child;
+            
+            mList.setOnScrollListener(this);
+            getSections();
+        }
+    }
+
+    public void onChildViewRemoved(View parent, View child) {
+        if (child == mList) {
+            mList = null;
+            mListAdapter = null;
+            mSections = null;
+        }
+    }
+    
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (mThumbVisible && ev.getAction() == MotionEvent.ACTION_DOWN) {
+            if (ev.getX() > getWidth() - mThumbW && ev.getY() >= mThumbY &&
+                    ev.getY() <= mThumbY + mThumbH) {
+                mDragging = true;
+                return true;
+            }            
+        }
+        return false;
+    }
+
+    private void scrollTo(float position) {
+        int count = mList.getCount();
+        mScrollCompleted = false;
+        final Object[] sections = mSections;
+        int sectionIndex;
+        if (sections != null && sections.length > 1) {
+            final int nSections = sections.length;
+            int section = (int) (position * nSections);
+            if (section >= nSections) {
+                section = nSections - 1;
+            }
+            sectionIndex = section;
+            final SectionIndexer baseAdapter = (SectionIndexer) mListAdapter;
+            int index = baseAdapter.getPositionForSection(section);
+            
+            // Given the expected section and index, the following code will
+            // try to account for missing sections (no names starting with..)
+            // It will compute the scroll space of surrounding empty sections
+            // and interpolate the currently visible letter's range across the
+            // available space, so that there is always some list movement while
+            // the user moves the thumb.
+            int nextIndex = count;
+            int prevIndex = index;
+            int prevSection = section;
+            int nextSection = section + 1;
+            // Assume the next section is unique
+            if (section < nSections - 1) {
+                nextIndex = baseAdapter.getPositionForSection(section + 1);
+            }
+            
+            // Find the previous index if we're slicing the previous section
+            if (nextIndex == index) {
+                // Non-existent letter
+                while (section > 0) {
+                    section--;
+                     prevIndex = baseAdapter.getPositionForSection(section);
+                     if (prevIndex != index) {
+                         prevSection = section;
+                         sectionIndex = section;
+                         break;
+                     }
+                }
+            }
+            // Find the next index, in case the assumed next index is not
+            // unique. For instance, if there is no P, then request for P's 
+            // position actually returns Q's. So we need to look ahead to make
+            // sure that there is really a Q at Q's position. If not, move 
+            // further down...
+            int nextNextSection = nextSection + 1;
+            while (nextNextSection < nSections &&
+                    baseAdapter.getPositionForSection(nextNextSection) == nextIndex) {
+                nextNextSection++;
+                nextSection++;
+            }
+            // Compute the beginning and ending scroll range percentage of the
+            // currently visible letter. This could be equal to or greater than
+            // (1 / nSections). 
+            float fPrev = (float) prevSection / nSections;
+            float fNext = (float) nextSection / nSections;
+            index = prevIndex + (int) ((nextIndex - prevIndex) * (position - fPrev) 
+                    / (fNext - fPrev));
+            // Don't overflow
+            if (index > count - 1) index = count - 1;
+            
+            mList.setSelectionFromTop(index + mListOffset, 0);
+        } else {
+            int index = (int) (position * count);
+            mList.setSelectionFromTop(index + mListOffset, 0);
+            sectionIndex = -1;
+        }
+
+        if (sectionIndex >= 0) {
+            String text = mSectionText = sections[sectionIndex].toString();
+            mDrawOverlay = (text.length() != 1 || text.charAt(0) != ' ') &&
+                    sectionIndex < sections.length;
+        } else {
+            mDrawOverlay = false;
+        }
+    }
+
+    private void cancelFling() {
+        // Cancel the list fling
+        MotionEvent cancelFling = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
+        mList.onTouchEvent(cancelFling);
+        cancelFling.recycle();
+    }
+    
+    @Override
+    public boolean onTouchEvent(MotionEvent me) {
+        if (me.getAction() == MotionEvent.ACTION_DOWN) {
+            if (me.getX() > getWidth() - mThumbW
+                    && me.getY() >= mThumbY 
+                    && me.getY() <= mThumbY + mThumbH) {
+                
+                mDragging = true;
+                if (mListAdapter == null && mList != null) {
+                    getSections();
+                }
+
+                cancelFling();
+                return true;
+            }
+        } else if (me.getAction() == MotionEvent.ACTION_UP) {
+            if (mDragging) {
+                mDragging = false;
+                final Handler handler = mHandler;
+                handler.removeCallbacks(mScrollFade);
+                handler.postDelayed(mScrollFade, 1000);
+                return true;
+            }
+        } else if (me.getAction() == MotionEvent.ACTION_MOVE) {
+            if (mDragging) {
+                final int viewHeight = getHeight();
+                mThumbY = (int) me.getY() - mThumbH + 10;
+                if (mThumbY < 0) {
+                    mThumbY = 0;
+                } else if (mThumbY + mThumbH > viewHeight) {
+                    mThumbY = viewHeight - mThumbH;
+                }
+                // If the previous scrollTo is still pending
+                if (mScrollCompleted) {
+                    scrollTo((float) mThumbY / (viewHeight - mThumbH));
+                }
+                return true;
+            }
+        }
+        
+        return super.onTouchEvent(me);
+    }
+    
+    public class ScrollFade implements Runnable {
+        
+        long mStartTime;
+        long mFadeDuration;
+        boolean mStarted;
+        static final int ALPHA_MAX = 255;
+        static final long FADE_DURATION = 200;
+        
+        void startFade() {
+            mFadeDuration = FADE_DURATION;
+            mStartTime = SystemClock.uptimeMillis();
+            mStarted = true;
+        }
+        
+        int getAlpha() {
+            if (!mStarted) {
+                return ALPHA_MAX;
+            }
+            int alpha;
+            long now = SystemClock.uptimeMillis();
+            if (now > mStartTime + mFadeDuration) {
+                alpha = 0;
+            } else {
+                alpha = (int) (ALPHA_MAX - ((now - mStartTime) * ALPHA_MAX) / mFadeDuration); 
+            }
+            return alpha;
+        }
+        
+        public void run() {
+            if (!mStarted) {
+                startFade();
+                invalidate();
+            }
+            
+            if (getAlpha() > 0) {
+                final int y = mThumbY;
+                final int viewWidth = getWidth();
+                invalidate(viewWidth - mThumbW, y, viewWidth, y + mThumbH);
+            } else {
+                mStarted = false;
+                removeThumb();
+            }
+        }
+    }
+}
diff --git a/src/com/android/contacts/RecentCallsListActivity.java b/src/com/android/contacts/RecentCallsListActivity.java
new file mode 100644
index 0000000..32ecd97
--- /dev/null
+++ b/src/com/android/contacts/RecentCallsListActivity.java
@@ -0,0 +1,790 @@
+/*
+ * Copyright (C) 2007 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.android.contacts;
+
+import android.app.ListActivity;
+import android.content.ActivityNotFoundException;
+import android.content.AsyncQueryHandler;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.pim.DateUtils;
+import android.provider.CallLog.Calls;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.Contacts.Intents.Insert;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.ResourceCursorAdapter;
+import android.widget.TextView;
+
+import com.android.internal.telephony.CallerInfo;
+import com.android.internal.telephony.ITelephony;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+/**
+ * Displays a list of call log entries.
+ */
+public class RecentCallsListActivity extends ListActivity 
+        implements View.OnCreateContextMenuListener {
+    private static final String TAG = "RecentCallsList";
+    
+    /** The projection to use when querying the call log table */
+    static final String[] CALL_LOG_PROJECTION = new String[] {
+            Calls._ID,
+            Calls.NUMBER,
+            Calls.DATE,
+            Calls.DURATION,
+            Calls.TYPE,
+            Calls.CACHED_NAME,
+            Calls.CACHED_NUMBER_TYPE,
+            Calls.CACHED_NUMBER_LABEL
+    };
+    
+    static final int ID_COLUMN_INDEX = 0;
+    static final int NUMBER_COLUMN_INDEX = 1;
+    static final int DATE_COLUMN_INDEX = 2;
+    static final int DURATION_COLUMN_INDEX = 3;
+    static final int CALL_TYPE_COLUMN_INDEX = 4;
+    static final int CALLER_NAME_COLUMN_INDEX = 5;
+    static final int CALLER_NUMBERTYPE_COLUMN_INDEX = 6;
+    static final int CALLER_NUMBERLABEL_COLUMN_INDEX = 7;
+
+    /** The projection to use when querying the phones table */
+    static final String[] PHONES_PROJECTION = new String[] {
+            Phones.PERSON_ID,
+            Phones.NAME,
+            Phones.TYPE,
+            Phones.LABEL,
+            Phones.NUMBER
+    };
+    
+    static final int PERSON_ID_COLUMN_INDEX = 0;
+    static final int NAME_COLUMN_INDEX = 1;
+    static final int PHONE_TYPE_COLUMN_INDEX = 2;
+    static final int LABEL_COLUMN_INDEX = 3;
+    static final int MATCHED_NUMBER_COLUMN_INDEX = 4;
+
+    private static final int MENU_ITEM_DELETE = 1;
+    private static final int MENU_ITEM_DELETE_ALL = 2;
+    private static final int MENU_ITEM_VIEW_CONTACTS = 3;
+    
+    private static final int QUERY_TOKEN = 53;
+    private static final int UPDATE_TOKEN = 54;
+
+    private RecentCallsAdapter mAdapter;
+    private QueryHandler mQueryHandler;
+    private String mVoiceMailNumber;
+    
+    private CharSequence[] mLabelArray;
+    
+    private Drawable mDrawableIncoming;
+    private Drawable mDrawableOutgoing;
+    private Drawable mDrawableMissed;    
+    
+    private static final class ContactInfo {
+        public long personId;
+        public String name;
+        public int type;
+        public String label;
+        public String number;
+
+        public static ContactInfo EMPTY = new ContactInfo();
+    }
+
+    public static final class RecentCallsListItemViews {
+        TextView line1View;
+        TextView line2View;
+        TextView durationView;
+        TextView dateView;
+        ImageView iconView;
+    }    
+    
+    private static final class CallerInfoQuery {
+        String number;
+        int position;
+        String name;
+        int numberType;
+        String numberLabel;
+    }
+
+    /** Adapter class to fill in data for the Call Log */
+    private final class RecentCallsAdapter extends ResourceCursorAdapter 
+            implements Runnable, ViewTreeObserver.OnPreDrawListener {
+        HashMap<String,ContactInfo> mContactInfo;
+        private LinkedList<CallerInfoQuery> mRequests;
+        private boolean mDone;
+        private boolean mLoading = true;
+        ViewTreeObserver.OnPreDrawListener mPreDrawListener;
+        private static final int REDRAW = 1;
+        private static final int START_THREAD = 2;
+        private boolean mFirst;
+        
+        public boolean onPreDraw() {
+            if (mFirst) {
+                mHandler.sendEmptyMessageDelayed(START_THREAD, 1000);
+                mFirst = false;
+            }
+            return true;            
+        }
+        
+        private Handler mHandler = new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case REDRAW:
+                        notifyDataSetChanged();
+                        break;
+                    case START_THREAD:
+                        startRequestProcessing();
+                        break;
+                }
+            }
+        };
+        
+        public RecentCallsAdapter() {
+            super(RecentCallsListActivity.this, R.layout.recent_calls_list_item, null);
+
+            mContactInfo = new HashMap<String,ContactInfo>();
+            mRequests = new LinkedList<CallerInfoQuery>();
+            mPreDrawListener = null;
+        }
+
+        void setLoading(boolean loading) {
+            mLoading = loading;
+        }
+        
+        @Override
+        public boolean isEmpty() {
+            if (mLoading) {
+                // We don't want the empty state to show when loading.
+                return false;
+            } else {
+                return super.isEmpty();
+            }
+        }
+
+        public ContactInfo getContactInfo(String number) {
+            return mContactInfo.get(number);
+        }
+
+        public void startRequestProcessing() {
+            mDone = false;
+            Thread callerIdThread = new Thread(this);
+            callerIdThread.setPriority(Thread.MIN_PRIORITY);
+            callerIdThread.start();
+        }
+
+        public void stopRequestProcessing() {
+            mDone = true;
+        }
+
+        public void clearCache() {
+            synchronized (mContactInfo) {
+                mContactInfo.clear();
+            }
+        }
+
+        private void updateCallLog(CallerInfoQuery ciq, ContactInfo ci) {
+            // Check if they are different. If not, don't update.
+            if (TextUtils.equals(ciq.name, ci.name) 
+                    && TextUtils.equals(ciq.numberLabel, ci.label)
+                    && ciq.numberType == ci.type) {
+                return;
+            }
+            ContentValues values = new ContentValues(3);
+            values.put(Calls.CACHED_NAME, ci.name);
+            values.put(Calls.CACHED_NUMBER_TYPE, ci.type);
+            values.put(Calls.CACHED_NUMBER_LABEL, ci.label);
+            RecentCallsListActivity.this.getContentResolver().update(
+                    Calls.CONTENT_URI, 
+                    values, Calls.NUMBER + "='" + ciq.number + "'", null);
+        }
+        
+        private void enqueueRequest(String number, int position,
+                String name, int numberType, String numberLabel) {
+            CallerInfoQuery ciq = new CallerInfoQuery();
+            ciq.number = number;
+            ciq.position = position;
+            ciq.name = name;
+            ciq.numberType = numberType;
+            ciq.numberLabel = numberLabel;
+            synchronized (mRequests) {
+                mRequests.add(ciq);
+                mRequests.notifyAll();
+            }
+        }
+        
+        private void queryContactInfo(CallerInfoQuery ciq) {
+            // First check if there was a prior request for the same number
+            // that was already satisfied
+            ContactInfo info = mContactInfo.get(ciq.number);
+            if (info != null && info != ContactInfo.EMPTY) {
+                synchronized (mRequests) {
+                    if (mRequests.isEmpty()) {
+                        mHandler.sendEmptyMessage(REDRAW);
+                    }
+                }
+            } else {
+                Cursor phonesCursor = 
+                    RecentCallsListActivity.this.getContentResolver().query(
+                            Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, 
+                                    ciq.number),
+                    PHONES_PROJECTION, null, null, null);
+                if (phonesCursor != null) {
+                    if (phonesCursor.moveToFirst()) {
+                        info = new ContactInfo();
+                        info.personId = phonesCursor.getLong(PERSON_ID_COLUMN_INDEX);
+                        info.name = phonesCursor.getString(NAME_COLUMN_INDEX);
+                        info.type = phonesCursor.getInt(PHONE_TYPE_COLUMN_INDEX);
+                        info.label = phonesCursor.getString(LABEL_COLUMN_INDEX);
+                        info.number = phonesCursor.getString(MATCHED_NUMBER_COLUMN_INDEX);
+                        
+                        mContactInfo.put(ciq.number, info);
+                        // Inform list to update this item, if in view
+                        synchronized (mRequests) {
+                            if (mRequests.isEmpty()) {
+                                mHandler.sendEmptyMessage(REDRAW);
+                            }
+                        }
+                    }
+                    phonesCursor.close();
+                }
+            }
+            if (info != null) {
+                updateCallLog(ciq, info);
+            }
+        }
+
+        /*
+         * Handles requests for contact name and number type
+         * @see java.lang.Runnable#run()
+         */
+        public void run() {
+            while (!mDone) {
+                CallerInfoQuery ciq = null;
+                synchronized (mRequests) {
+                    if (!mRequests.isEmpty()) {
+                        ciq = mRequests.removeFirst();
+                    } else {
+                        try {
+                            mRequests.wait(1000);
+                        } catch (InterruptedException ie) {
+                            // Ignore and continue processing requests
+                        }                        
+                    }
+                }
+                if (ciq != null) {
+                    queryContactInfo(ciq);
+                }
+            }
+        }
+        
+        @Override
+        public View newView(Context context, Cursor cursor, ViewGroup parent) {
+            View view = super.newView(context, cursor, parent);
+            
+            // Get the views to bind to
+            RecentCallsListItemViews views = new RecentCallsListItemViews();
+            views.line1View = (TextView) view.findViewById(R.id.line1);
+            views.line2View = (TextView) view.findViewById(R.id.line2);
+            views.durationView = (TextView) view.findViewById(R.id.duration);
+            views.dateView = (TextView) view.findViewById(R.id.date);
+            views.iconView = (ImageView) view.findViewById(R.id.call_type_icon);
+
+            view.setTag(views);
+
+            return view;
+        }
+
+        
+        @Override
+        public void bindView(View view, Context context, Cursor c) {
+            final RecentCallsListItemViews views = (RecentCallsListItemViews) view.getTag();
+
+            String number = c.getString(NUMBER_COLUMN_INDEX);
+            String callerName = c.getString(CALLER_NAME_COLUMN_INDEX);
+            int callerNumberType = c.getInt(CALLER_NUMBERTYPE_COLUMN_INDEX);
+            String callerNumberLabel = c.getString(CALLER_NUMBERLABEL_COLUMN_INDEX);
+            
+            // Lookup contacts with this number
+            ContactInfo info = mContactInfo.get(number);
+            if (info == null) {
+                // Mark it as empty and queue up a request to find the name
+                // The db request should happen on a non-UI thread
+                info = ContactInfo.EMPTY;
+                mContactInfo.put(number, info);
+                enqueueRequest(number, c.getPosition(),
+                        callerName, callerNumberType, callerNumberLabel);
+            } else if (info != ContactInfo.EMPTY) { // Has been queried
+                // Check if any data is different from the data cached in the
+                // calls db. If so, queue the request so that we can update
+                // the calls db.
+                if (!TextUtils.equals(info.name, callerName) 
+                        || info.type != callerNumberType
+                        || !TextUtils.equals(info.label, callerNumberLabel)) {
+                    // Something is amiss, so sync up.
+                    enqueueRequest(number, c.getPosition(), 
+                            callerName, callerNumberType, callerNumberLabel);
+                }
+            }
+
+            String name = info.name;
+            int ntype = info.type;
+            String label = info.label;
+            // If there's no name cached in our hashmap, but there's one in the
+            // calls db, use the one in the calls db. Otherwise the name in our
+            // hashmap is more recent, so it has precedence.
+            if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(callerName)) {
+                name = callerName;
+                ntype = callerNumberType;
+                label = callerNumberLabel;
+            }
+            // Set the text lines
+            if (!TextUtils.isEmpty(name)) {
+                views.line1View.setText(name);
+                CharSequence numberLabel = Phones.getDisplayLabel(context, ntype, label, 
+                        mLabelArray);
+                if (!TextUtils.isEmpty(numberLabel)) {
+                    views.line2View.setText(numberLabel);
+                } else {
+                    views.line2View.setText(number);
+                }
+
+                // Set the presence icon
+/*
+                int serverStatus;
+                if (!c.isNull(SERVER_STATUS_COLUMN_INDEX)) {
+                    serverStatus = c.getInt(SERVER_STATUS_COLUMN_INDEX);
+                    views.line2View.setCompoundDrawablesWithIntrinsicBounds(
+                            getResources().getDrawable(
+                                    Presence.getPresenceIconResourceId(serverStatus)),
+                            null, null, null);
+                } else {
+                    views.line2View.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
+                }
+*/
+            } else {
+                if (number.equals(CallerInfo.UNKNOWN_NUMBER)) {
+                    number = getString(R.string.unknown);
+                } else if (number.equals(CallerInfo.PRIVATE_NUMBER)) {
+                    number = getString(R.string.private_num);
+                } else if (number.equals(mVoiceMailNumber)) {
+                    number = getString(R.string.voicemail);
+                }
+
+                views.line1View.setText(number);
+                views.line2View.setText(null);
+
+                // Clear the presence icon
+//                views.line2View.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
+            }
+
+            int type = c.getInt(CALL_TYPE_COLUMN_INDEX);
+            long date = c.getLong(DATE_COLUMN_INDEX);
+
+            // Set the duration
+            if (type == Calls.MISSED_TYPE) {
+                views.durationView.setVisibility(View.GONE);
+            } else {
+                views.durationView.setVisibility(View.VISIBLE);
+                views.durationView.setText(DateUtils.formatElapsedTime(c.getLong(DURATION_COLUMN_INDEX)));
+            }
+
+            // Set the time and date
+            views.dateView.setText(DateUtils.getRelativeTimeSpanString(date));
+
+            // Set the icon
+            switch (type) {
+                case Calls.INCOMING_TYPE:
+                    views.iconView.setImageDrawable(mDrawableIncoming);
+                    break;
+
+                case Calls.OUTGOING_TYPE:
+                    views.iconView.setImageDrawable(mDrawableOutgoing);
+                    break;
+
+                case Calls.MISSED_TYPE:
+                    views.iconView.setImageDrawable(mDrawableMissed);
+                    break;
+            }
+            // Listen for the first draw 
+            if (mPreDrawListener == null) {
+                mFirst = true;
+                mPreDrawListener = this;
+                view.getViewTreeObserver().addOnPreDrawListener(this);
+            }
+        }
+    }
+    
+    private final class QueryHandler extends AsyncQueryHandler {
+        public QueryHandler(Context context) {
+            super(context.getContentResolver());
+        }
+
+        @Override
+        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+            if (!isFinishing()) {
+                mAdapter.setLoading(false);
+                mAdapter.changeCursor(cursor);    
+            } else {
+                cursor.close();
+            }
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle state) {
+        super.onCreate(state);
+
+        setContentView(R.layout.recent_calls);
+        
+        mDrawableIncoming = getResources().getDrawable(android.R.drawable.sym_call_incoming);
+        mDrawableOutgoing = getResources().getDrawable(android.R.drawable.sym_call_outgoing);
+        mDrawableMissed = getResources().getDrawable(android.R.drawable.sym_call_missed);
+        mLabelArray = getResources().getTextArray(com.android.internal.R.array.phoneTypes);
+        
+        // Typing here goes to the dialer
+        setDefaultKeyMode(DEFAULT_KEYS_DIALER);
+
+        mAdapter = new RecentCallsAdapter();
+        getListView().setOnCreateContextMenuListener(this);
+        setListAdapter(mAdapter);
+
+        mVoiceMailNumber = ((TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE))
+                .getVoiceMailNumber();
+        mQueryHandler = new QueryHandler(this);
+    }
+
+    @Override
+    protected void onResume() {
+        // The adapter caches looked up numbers, clear it so they will get
+        // looked up again.
+        if (mAdapter != null) {
+            mAdapter.clearCache();
+        }
+
+        startQuery();
+        resetNewCallsFlag();
+        
+        super.onResume();
+        try {
+            ITelephony.Stub.asInterface(ServiceManager.getService("phone"))
+                    .cancelMissedCallsNotification();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to clear missed calls notification due to remote excetpion");
+        }
+        mAdapter.mPreDrawListener = null; // Let it restart the thread after next draw
+    }
+    
+    @Override
+    protected void onPause() {
+        super.onPause();
+        
+        // Kill the requests thread
+        mAdapter.stopRequestProcessing();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        Cursor cursor = mAdapter.getCursor();
+        if (cursor != null && !cursor.isClosed()) {
+            cursor.close();
+        }
+    }
+
+    private void resetNewCallsFlag() {
+        // Mark all "new" missed calls as not new anymore
+        StringBuilder where = new StringBuilder("type=");
+        where.append(Calls.MISSED_TYPE);
+        where.append(" AND new=1");
+
+        ContentValues values = new ContentValues(1);
+        values.put(Calls.NEW, "0");
+        mQueryHandler.startUpdate(UPDATE_TOKEN, null, Calls.CONTENT_URI, 
+                values, where.toString(), null);
+    }
+
+    private void startQuery() {
+        mAdapter.setLoading(true);
+        
+        // Cancel any pending queries
+        mQueryHandler.cancelOperation(QUERY_TOKEN);
+        mQueryHandler.startQuery(QUERY_TOKEN, null, Calls.CONTENT_URI, 
+                CALL_LOG_PROJECTION, null, null, Calls.DEFAULT_SORT_ORDER);
+    }
+    
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        menu.add(0, MENU_ITEM_DELETE_ALL, 0, R.string.recentCalls_deleteAll)
+                .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
+        return true;
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfoIn) {
+        AdapterView.AdapterContextMenuInfo menuInfo;
+        try {
+             menuInfo = (AdapterView.AdapterContextMenuInfo) menuInfoIn;
+        } catch (ClassCastException e) {
+            Log.e(TAG, "bad menuInfoIn", e);
+            return;
+        }
+
+        Cursor cursor = (Cursor) mAdapter.getItem(menuInfo.position);
+
+        String number = cursor.getString(NUMBER_COLUMN_INDEX);
+        Uri numberUri = null;
+        boolean isVoicemail = false;
+        if (number.equals(CallerInfo.UNKNOWN_NUMBER)) {
+            number = getString(R.string.unknown);
+        } else if (number.equals(CallerInfo.PRIVATE_NUMBER)) {
+            number = getString(R.string.private_num);
+        } else if (number.equals(mVoiceMailNumber)) {
+            number = getString(R.string.voicemail);
+            numberUri = Uri.parse("voicemail:x");
+            isVoicemail = true;
+        } else {
+            numberUri = Uri.fromParts("tel", number, null);
+        }
+
+        ContactInfo info = mAdapter.getContactInfo(number);
+        boolean contactInfoPresent = (info != null && info != ContactInfo.EMPTY); 
+        if (contactInfoPresent) {
+            menu.setHeaderTitle(info.name);
+        } else {
+            menu.setHeaderTitle(number);
+        }
+
+        if (numberUri != null) {
+            Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, numberUri);
+            menu.add(0, 0, 0, getResources().getString(R.string.recentCalls_callNumber, number))
+                    .setIntent(intent);
+        }
+
+        if (contactInfoPresent) {
+            menu.add(0, 0, 0, R.string.menu_viewContact)
+                    .setIntent(new Intent(Intent.ACTION_VIEW,
+                            ContentUris.withAppendedId(People.CONTENT_URI, info.personId)));
+        }
+
+        if (numberUri != null && !isVoicemail) {
+            menu.add(0, 0, 0, R.string.recentCalls_editNumberBeforeCall)
+                    .setIntent(new Intent(Intent.ACTION_DIAL, numberUri));
+            menu.add(0, 0, 0, R.string.menu_sendTextMessage)
+                    .setIntent(new Intent(Intent.ACTION_SENDTO,
+                            Uri.fromParts("sms", number, null)));
+        }
+        if (!contactInfoPresent && numberUri != null && !isVoicemail) {
+            Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+            intent.setType(People.CONTENT_ITEM_TYPE);
+            intent.putExtra(Insert.PHONE, number);
+            menu.add(0, 0, 0, R.string.recentCalls_addToContact)
+                    .setIntent(intent);
+        }
+        menu.add(0, MENU_ITEM_DELETE, 0, R.string.recentCalls_removeFromRecentList);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_ITEM_DELETE_ALL: {
+                getContentResolver().delete(Calls.CONTENT_URI, null, null);
+                //TODO The change notification should do this automatically, but it isn't working
+                // right now. Remove this when the change notification is working properly. 
+                startQuery();
+                return true;
+            }
+
+            case MENU_ITEM_VIEW_CONTACTS: {
+                Intent intent = new Intent(Intent.ACTION_VIEW, People.CONTENT_URI);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                startActivity(intent);
+                return true;
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        // Convert the menu info to the proper type
+        AdapterView.AdapterContextMenuInfo menuInfo;
+        try {
+             menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+        } catch (ClassCastException e) {
+            Log.e(TAG, "bad menuInfoIn", e);
+            return false;
+        }
+
+        switch (item.getItemId()) {
+            case MENU_ITEM_DELETE: {
+                Cursor cursor = mAdapter.getCursor();
+                if (cursor != null) {
+                    cursor.moveToPosition(menuInfo.position);
+                    cursor.deleteRow();
+                }
+                return true;
+            }
+        }
+        return super.onContextItemSelected(item);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_CALL: {
+                long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
+                if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
+                    // Launch voice dialer
+                    Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    try {
+                        startActivity(intent);
+                    } catch (ActivityNotFoundException e) {
+                    }
+                    return true;
+                }
+            }
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_CALL:
+                try {
+                    ITelephony phone = ITelephony.Stub.asInterface(
+                            ServiceManager.checkService("phone"));
+                    if (phone != null && !phone.isIdle()) {
+                        // Let the super class handle it
+                        break;
+                    }
+                } catch (RemoteException re) {
+                    // Fall through and try to call the contact
+                }
+                
+                callEntry(getListView().getSelectedItemPosition());
+                return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+    
+    /*
+     * Get the number from the Contacts, if available, since sometimes
+     * the number provided by caller id may not be formatted properly
+     * depending on the carrier (roaming) in use at the time of the 
+     * incoming call.
+     * Logic : If the caller-id number starts with a "+", use it
+     *         Else if the number in the contacts starts with a "+", use that one
+     *         Else if the number in the contacts is longer, use that one
+     */
+    private String getBetterNumberFromContacts(String number) {
+        String matchingNumber = null;
+        // Look in the cache first. If it's not found then query the Phones db
+        ContactInfo ci = mAdapter.mContactInfo.get(number);
+        if (ci != null && ci != ContactInfo.EMPTY) {
+            matchingNumber = ci.number;
+        } else {
+            try {
+                Cursor phonesCursor = 
+                    RecentCallsListActivity.this.getContentResolver().query(
+                            Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, 
+                                    number),
+                    PHONES_PROJECTION, null, null, null);
+                if (phonesCursor != null) {
+                    if (phonesCursor.moveToFirst()) {
+                        matchingNumber = phonesCursor.getString(MATCHED_NUMBER_COLUMN_INDEX);
+                    }
+                    phonesCursor.close();
+                }
+            } catch (Exception e) {
+                // Use the number from the call log
+            }
+        }
+        if (!TextUtils.isEmpty(matchingNumber) && 
+                (matchingNumber.startsWith("+") 
+                        || matchingNumber.length() > number.length())) {
+            number = matchingNumber;
+        }
+        return number;
+    }
+    
+    private void callEntry(int position) {
+        if (position < 0) {
+            // In touch mode you may often not have something selected, so
+            // just call the first entry to make sure that [send] [send] calls the
+            // most recent entry.
+            position = 0;
+        }
+        final Cursor cursor = mAdapter.getCursor();
+        if (cursor != null && cursor.moveToPosition(position)) {
+            String number = cursor.getString(NUMBER_COLUMN_INDEX);
+            if (TextUtils.isEmpty(number)
+                    || number.equals(CallerInfo.UNKNOWN_NUMBER)
+                    || number.equals(CallerInfo.PRIVATE_NUMBER)) {
+                // This number can't be called, do nothing
+                return;
+            }
+
+            int callType = cursor.getInt(CALL_TYPE_COLUMN_INDEX);
+            if (!number.startsWith("+") && 
+                    (callType == Calls.INCOMING_TYPE 
+                            || callType == Calls.MISSED_TYPE)) {
+                // If the caller-id matches a contact with a better qualified number, use it
+                number = getBetterNumberFromContacts(number);
+            }
+            Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+                    Uri.fromParts("tel", number, null));
+            intent.setFlags(
+                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            startActivity(intent);
+        }
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        callEntry(position);
+    }
+}
diff --git a/src/com/android/contacts/SpecialCharSequenceMgr.java b/src/com/android/contacts/SpecialCharSequenceMgr.java
new file mode 100644
index 0000000..e23d460
--- /dev/null
+++ b/src/com/android/contacts/SpecialCharSequenceMgr.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2006 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.android.contacts;
+
+import com.android.internal.telephony.ITelephony;
+
+import android.app.AlertDialog;
+import android.app.KeyguardManager;
+import android.app.ProgressDialog;
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Telephony.Intents;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.Toast;
+
+/**
+ * Helper class to listen for some magic character sequences
+ * that are handled specially by the dialer.
+ */
+public class SpecialCharSequenceMgr {
+    private static final String TAG = "SpecialCharSequenceMgr";
+    private static final String MMI_IMEI_DISPLAY = "*#06#";
+
+    /** This class is never instantiated. */
+    private SpecialCharSequenceMgr() {
+    }
+
+    static boolean handleChars(Context context, String input, EditText textField) {
+        return handleChars(context, input, false, textField);
+    }
+
+    static boolean handleChars(Context context, String input) {
+        return handleChars(context, input, false, null);
+    }
+
+    static boolean handleChars(Context context, String input, boolean useSystemWindow,
+            EditText textField) {
+
+        //get rid of the separators so that the string gets parsed correctly
+        String dialString = PhoneNumberUtils.stripSeparators(input);
+
+        if (handleIMEIDisplay(context, dialString, useSystemWindow)
+                || handlePinEntry(context, dialString)
+                || handleAdnEntry(context, dialString, textField)
+                || handleSecretCode(context, dialString)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Handles secret codes to launch arbitrary activities in the form of *#*#<code>#*#*.
+     * If a secret code is encountered an Intent is started with the android_secret_code://<code>
+     * URI.
+     *
+     * @param context the context to use
+     * @param input the text to check for a secret code in
+     * @return true if a secret code was encountered
+     */
+    static boolean handleSecretCode(Context context, String input) {
+        // Secret codes are in the form *#*#<code>#*#*
+        int len = input.length();
+        if (len > 8 && input.startsWith("*#*#") && input.endsWith("#*#*")) {
+            Intent intent = new Intent(Intents.SECRET_CODE_ACTION,
+                    Uri.parse("android_secret_code://" + input.substring(4, len - 4)));
+            context.sendBroadcast(intent);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Handle ADN requests by filling in the SIM contact number into the requested
+     * EditText.
+     *
+     * This code works alongside the Asynchronous query handler {@link QueryHandler}
+     * and query cancel handler implemented in {@link SimContactQueryCookie}.
+     */
+    static boolean handleAdnEntry(Context context, String input, EditText textField) {
+        /* ADN entries are of the form "N(N)(N)#" */
+
+        // if the phone is keyguard-restricted, then just ignore this
+        // input.  We want to make sure that sim card contacts are NOT
+        // exposed unless the phone is unlocked, and this code can be
+        // accessed from the emergency dialer.
+        KeyguardManager keyguardManager =
+                (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+        if (keyguardManager.inKeyguardRestrictedInputMode()) {
+            return false;
+        }
+
+        int len = input.length();
+        if ((len > 1) && (len < 5) && (input.endsWith("#"))) {
+            try {
+                // get the ordinal number of the sim contact
+                int index = Integer.parseInt(input.substring(0, len-1));
+
+                // The original code that navigated to a SIM Contacts list view did not
+                // highlight the requested contact correctly, a requirement for PTCRB
+                // certification.  This behaviour is consistent with the UI paradigm
+                // for touch-enabled lists, so it does not make sense to try to work
+                // around it.  Instead we fill in the the requested phone number into
+                // the dialer text field.
+
+                // create the async query handler
+                QueryHandler handler = new QueryHandler (context.getContentResolver());
+
+                // create the cookie object
+                SimContactQueryCookie sc = new SimContactQueryCookie(index - 1, handler,
+                        ADN_QUERY_TOKEN);
+
+                // setup the cookie fields
+                sc.contactNum = index - 1;
+                sc.setTextField(textField);
+
+                // create the progress dialog
+                sc.progressDialog = new ProgressDialog(context);
+                sc.progressDialog.setTitle(R.string.simContacts_title);
+                sc.progressDialog.setMessage(context.getText(R.string.simContacts_emptyLoading));
+                sc.progressDialog.setIndeterminate(true);
+                sc.progressDialog.setCancelable(true);
+                sc.progressDialog.setOnCancelListener(sc);
+                sc.progressDialog.getWindow().addFlags(
+                        WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+
+                // display the progress dialog
+                sc.progressDialog.show();
+
+                // run the query.
+                handler.startQuery(ADN_QUERY_TOKEN, sc, Uri.parse("content://sim/adn"),
+                        new String[]{ADN_PHONE_NUMBER_COLUMN_NAME}, null, null, null);
+                return true;
+            } catch (NumberFormatException ex) {
+                // Ignore
+            }
+        }
+        return false;
+    }
+
+    static boolean handlePinEntry(Context context, String input) {
+        if ((input.startsWith("**04") || input.startsWith("**05")) && input.endsWith("#")) {
+            try {
+                return ITelephony.Stub.asInterface(ServiceManager.getService("phone"))
+                        .handlePinMmi(input);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to handlePinMmi due to remote exception");
+                return false;
+            }
+        }
+        return false;
+    }
+
+    static boolean handleIMEIDisplay(Context context, String input, boolean useSystemWindow) {
+        if (input.equals(MMI_IMEI_DISPLAY)) {
+            showIMEIPanel(context, useSystemWindow);
+            return true;
+        }
+
+        return false;
+    }
+
+    static void showIMEIPanel(Context context, boolean useSystemWindow) {
+        String imeiStr = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE))
+                .getDeviceId();
+
+        AlertDialog alert = new AlertDialog.Builder(context)
+                .setTitle(R.string.imei)
+                .setMessage(imeiStr)
+                .setPositiveButton(R.string.ok, null)
+                .setCancelable(false)
+                .show();
+        alert.getWindow().setType(WindowManager.LayoutParams.TYPE_PRIORITY_PHONE);
+    }
+
+    /*******
+     * This code is used to handle SIM Contact queries
+     *******/
+    private static final String ADN_PHONE_NUMBER_COLUMN_NAME = "number";
+    private static final String ADN_NAME_COLUMN_NAME = "name";
+    private static final int ADN_QUERY_TOKEN = -1;
+
+    /**
+     * Cookie object that contains everything we need to communicate to the
+     * handler's onQuery Complete, as well as what we need in order to cancel
+     * the query (if requested).
+     *
+     * Note, access to the textField field is going to be synchronized, because
+     * the user can request a cancel at any time through the UI.
+     */
+    private static class SimContactQueryCookie implements DialogInterface.OnCancelListener{
+        public ProgressDialog progressDialog;
+        public int contactNum;
+
+        // Used to identify the query request.
+        private int mToken;
+        private QueryHandler mHandler;
+
+        // The text field we're going to update
+        private EditText textField;
+
+        public SimContactQueryCookie(int number, QueryHandler handler, int token) {
+            contactNum = number;
+            mHandler = handler;
+            mToken = token;
+        }
+
+        /**
+         * Synchronized getter for the EditText.
+         */
+        public synchronized EditText getTextField() {
+            return textField;
+        }
+
+        /**
+         * Synchronized setter for the EditText.
+         */
+        public synchronized void setTextField(EditText text) {
+            textField = text;
+        }
+
+        /**
+         * Cancel the ADN query by stopping the operation and signaling
+         * the cookie that a cancel request is made.
+         */
+        public synchronized void onCancel(DialogInterface dialog) {
+            // close the progress dialog
+            if (progressDialog != null) {
+                progressDialog.dismiss();
+            }
+
+            // setting the textfield to null ensures that the UI does NOT get
+            // updated.
+            textField = null;
+
+            // Cancel the operation if possible.
+            mHandler.cancelOperation(mToken);
+        }
+    }
+
+    /**
+     * Asynchronous query handler that services requests to look up ADNs
+     *
+     * Queries originate from {@link handleAdnEntry}.
+     */
+    private static class QueryHandler extends AsyncQueryHandler {
+
+        public QueryHandler(ContentResolver cr) {
+            super(cr);
+        }
+
+        /**
+         * Override basic onQueryComplete to fill in the textfield when
+         * we're handed the ADN cursor.
+         */
+        @Override
+        protected void onQueryComplete(int token, Object cookie, Cursor c) {
+            SimContactQueryCookie sc = (SimContactQueryCookie) cookie;
+
+            // close the progress dialog.
+            sc.progressDialog.dismiss();
+
+            // get the EditText to update or see if the request was cancelled.
+            EditText text = sc.getTextField();
+
+            // if the textview is valid, and the cursor is valid and postionable
+            // on the Nth number, then we update the text field and display a
+            // toast indicating the caller name.
+            if ((c != null) && (text != null) && (c.moveToPosition(sc.contactNum))) {
+                String name = c.getString(c.getColumnIndexOrThrow(ADN_NAME_COLUMN_NAME));
+                String number = c.getString(c.getColumnIndexOrThrow(ADN_PHONE_NUMBER_COLUMN_NAME));
+
+                // fill the text in.
+                text.getText().replace(0, 0, number);
+
+                // display the name as a toast
+                Context context = sc.progressDialog.getContext();
+                name = context.getString(R.string.menu_callNumber, name);
+                Toast.makeText(context, name, Toast.LENGTH_SHORT)
+                    .show();
+            }
+        }
+    }
+}
diff --git a/src/com/android/contacts/TwelveKeyDialer.java b/src/com/android/contacts/TwelveKeyDialer.java
new file mode 100644
index 0000000..caa77a1
--- /dev/null
+++ b/src/com/android/contacts/TwelveKeyDialer.java
@@ -0,0 +1,595 @@
+/*
+ * Copyright (C) 2007 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.android.contacts;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.media.ToneGenerator;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.Contacts.PhonesColumns;
+import android.provider.Contacts.Intents.Insert;
+import android.telephony.PhoneNumberFormattingTextWatcher;
+import android.telephony.PhoneNumberUtils;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.method.DialerKeyListener;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.EditText;
+
+/**
+ * Dialer activity that displays the typical twelve key interface.
+ */
+public class TwelveKeyDialer extends Activity implements View.OnClickListener,
+        View.OnLongClickListener, View.OnKeyListener, TextWatcher {
+
+    private static final String TAG = "TwelveKeyDialer";
+    
+    private static final int STOP_TONE = 1;
+
+    /** The length of DTMF tones in milliseconds */
+    private static final int TONE_LENGTH_MS = 150;
+    
+    /** The DTMF tone volume relative to other sounds in the stream */
+    private static final int TONE_RELATIVE_VOLUME = 50;
+
+    private EditText mDigits;
+    private View mDelete;
+    private MenuItem mAddToContactMenuItem;
+    private ToneGenerator mToneGenerator;
+    private Object mToneGeneratorLock = new Object();
+    private Drawable mDigitsBackground;
+    private Drawable mDigitsEmptyBackground;
+    private Drawable mDeleteBackground;
+    private Drawable mDeleteEmptyBackground;
+    
+    // determines if we want to playback local DTMF tones.
+    private boolean mDTMFToneEnabled;
+    
+    /** Identifier for the "Add Call" intent extra. */
+    static final String ADD_CALL_MODE_KEY = "add_call_mode";
+    /** Indicates if we are opening this dialer to add a call from the InCallScreen. */
+    private boolean mIsAddCallMode;
+
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+        // Do nothing
+    }
+
+    public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
+        // Do nothing
+        // DTMF Tones do not need to be played here any longer - 
+        // the DTMF dialer handles that functionality now.
+    }
+
+    public void afterTextChanged(Editable input) {
+        if (SpecialCharSequenceMgr.handleChars(this, input.toString(), mDigits)) {
+            // A special sequence was entered, clear the digits
+            mDigits.getText().clear();
+        }
+
+        // Set the proper background for the dial input area
+        if (mDigits.length() != 0) {
+            mDelete.setBackgroundDrawable(mDeleteBackground);
+            mDigits.setBackgroundDrawable(mDigitsBackground);
+            mDigits.setCompoundDrawablesWithIntrinsicBounds(
+                    getResources().getDrawable(R.drawable.ic_dial_number), null, null, null);
+        } else {
+            mDelete.setBackgroundDrawable(mDeleteEmptyBackground);
+            mDigits.setBackgroundDrawable(mDigitsEmptyBackground);
+            mDigits.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Set the content view
+        setContentView(getContentViewResource());
+
+        // Load up the resources for the text field and delete button
+        Resources r = getResources();
+        mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
+        //mDigitsBackground.setDither(true);
+        mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
+        //mDigitsEmptyBackground.setDither(true);
+        mDeleteBackground = r.getDrawable(R.drawable.btn_dial_delete_active);
+        //mDeleteBackground.setDither(true);
+        mDeleteEmptyBackground = r.getDrawable(R.drawable.btn_dial_delete);
+        //mDeleteEmptyBackground.setDither(true);
+
+        mDigits = (EditText) findViewById(R.id.digits);
+        mDigits.setKeyListener(DialerKeyListener.getInstance());
+        mDigits.setOnClickListener(this);
+        mDigits.setOnKeyListener(this);
+        maybeAddNumberFormatting();
+
+        // Check for the presence of the keypad
+        View view = findViewById(R.id.one);
+        if (view != null) {
+            setupKeypad();
+        }
+
+        view = findViewById(R.id.backspace);
+        view.setOnClickListener(this);
+        view.setOnLongClickListener(this);
+        mDelete = view;
+
+        if (!resolveIntent() && icicle != null) {
+            super.onRestoreInstanceState(icicle);
+        }
+        
+        // if the mToneGenerator creation fails, just continue without it.  It is 
+        // a local audio signal, and is not as important as the dtmf tone itself.
+        synchronized(mToneGeneratorLock) {
+            if (mToneGenerator == null) {
+                try {
+                    mToneGenerator = new ToneGenerator(AudioManager.STREAM_RING, 
+                            TONE_RELATIVE_VOLUME);
+                } catch (RuntimeException e) {
+                    Log.w(TAG, "Exception caught while creating local tone generator: " + e);
+                    mToneGenerator = null;
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        synchronized(mToneGeneratorLock) {
+            if (mToneGenerator != null) {
+                mToneStopper.removeMessages(STOP_TONE);
+                mToneGenerator.release();
+                mToneGenerator = null;
+            }
+        }
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle icicle) {
+        // Do nothing, state is restored in onCreate() if needed
+    }
+    
+    protected void maybeAddNumberFormatting() {
+        mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
+    }
+    
+    /**
+     * Overridden by subclasses to control the resource used by the content view. 
+     */
+    protected int getContentViewResource() {
+        return R.layout.twelve_key_dialer;
+    }
+
+    private boolean resolveIntent() {
+        boolean ignoreState = false;
+
+        // Find the proper intent
+        final Intent intent;
+        if (isChild()) {
+            intent = getParent().getIntent();
+            ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
+        } else {
+            intent = getIntent();
+        }
+
+        // by default we are not adding a call.
+        mIsAddCallMode = false;
+        
+        // Resolve the intent
+        final String action = intent.getAction();
+        if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
+            // see if we are "adding a call" from the InCallScreen; false by default.
+            mIsAddCallMode = intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
+            Uri uri = intent.getData();
+            if (uri != null) {
+                if ("tel".equals(uri.getScheme())) {
+                    // Put the requested number into the input area
+                    String data = uri.getSchemeSpecificPart();
+                    setFormattedDigits(data);
+                } else {
+                    String type = intent.getType();
+                    if (People.CONTENT_ITEM_TYPE.equals(type)
+                            || Phones.CONTENT_ITEM_TYPE.equals(type)) {
+                        // Query the phone number
+                        Cursor c = getContentResolver().query(intent.getData(),
+                                new String[] {PhonesColumns.NUMBER}, null, null, null);
+                        if (c != null) {
+                            if (c.moveToFirst()) {
+                                // Put the number into the input area
+                                setFormattedDigits(c.getString(0));
+                            }
+                            c.close();
+                        }
+                    }
+                }
+            }
+        }
+
+        return ignoreState;
+    }
+
+    protected void setFormattedDigits(String data) {
+        // strip the non-dialable numbers out of the data string.
+        String dialString = PhoneNumberUtils.extractNetworkPortion(data);
+        dialString = PhoneNumberUtils.formatNumber(dialString);
+        if (!TextUtils.isEmpty(dialString)) {
+            Editable digits = mDigits.getText();
+            digits.replace(0, digits.length(), dialString);
+            mDigits.setCompoundDrawablesWithIntrinsicBounds(
+                    getResources().getDrawable(R.drawable.ic_dial_number), null, null, null);
+        }
+    }
+
+    @Override
+    protected void onNewIntent(Intent newIntent) {
+        setIntent(newIntent);
+        resolveIntent();
+    }
+    
+    @Override
+    protected void onPostCreate(Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+
+        // This can't be done in onCreate(), since the auto-restoring of the digits
+        // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
+        // is called. This method will be called every time the activity is created, and
+        // will always happen after onRestoreSavedInstanceState().
+        mDigits.addTextChangedListener(this);
+    }
+    
+    private void setupKeypad() {
+        // Setup the listeners for the buttons
+        View view = findViewById(R.id.one);
+        view.setOnClickListener(this);
+        view.setOnLongClickListener(this);
+
+        findViewById(R.id.two).setOnClickListener(this);
+        findViewById(R.id.three).setOnClickListener(this);
+        findViewById(R.id.four).setOnClickListener(this);
+        findViewById(R.id.five).setOnClickListener(this);
+        findViewById(R.id.six).setOnClickListener(this);
+        findViewById(R.id.seven).setOnClickListener(this);
+        findViewById(R.id.eight).setOnClickListener(this);
+        findViewById(R.id.nine).setOnClickListener(this);
+        findViewById(R.id.star).setOnClickListener(this);
+
+        view = findViewById(R.id.zero);
+        view.setOnClickListener(this);
+        view.setOnLongClickListener(this);
+
+        findViewById(R.id.pound).setOnClickListener(this);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        
+        // retrieve the DTMF tone play back setting.
+        mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
+                Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
+
+        // if the mToneGenerator creation fails, just continue without it.  It is 
+        // a local audio signal, and is not as important as the dtmf tone itself.
+        synchronized(mToneGeneratorLock) {
+            if (mToneGenerator == null) {
+                try {
+                    mToneGenerator = new ToneGenerator(AudioManager.STREAM_RING, 
+                            TONE_RELATIVE_VOLUME);
+                } catch (RuntimeException e) {
+                    Log.w(TAG, "Exception caught while creating local tone generator: " + e);
+                    mToneGenerator = null;
+                }
+            }
+        }
+        
+        Activity parent = getParent();
+        // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
+        // digits in the dialer field.
+        if (parent != null && parent instanceof DialtactsActivity) {
+            Uri dialUri = ((DialtactsActivity) parent).getAndClearDialUri();
+            if (dialUri != null) {
+                resolveIntent();
+            }
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        synchronized(mToneGeneratorLock) {
+            if (mToneGenerator != null) {
+                mToneStopper.removeMessages(STOP_TONE);
+                mToneGenerator.release();
+                mToneGenerator = null;
+            }
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        mAddToContactMenuItem = menu.add(0, 0, 0, R.string.recentCalls_addToContact)
+                .setIcon(android.R.drawable.ic_menu_add);
+
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        CharSequence digits = mDigits.getText();
+        if (digits == null || !TextUtils.isGraphic(digits)) {
+            mAddToContactMenuItem.setVisible(false);
+        } else {
+            // Put the current digits string into an intent
+            Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+            intent.putExtra(Insert.PHONE, mDigits.getText());
+            intent.setType(People.CONTENT_ITEM_TYPE);
+            mAddToContactMenuItem.setIntent(intent);
+            mAddToContactMenuItem.setVisible(true);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_CALL: {
+                long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
+                if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
+                    // Launch voice dialer
+                    Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    try {
+                        startActivity(intent);
+                    } catch (ActivityNotFoundException e) {
+                    }
+                }
+                return true;
+            }
+            case KeyEvent.KEYCODE_1: {
+                long timeDiff = SystemClock.uptimeMillis() - event.getDownTime(); 
+                if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
+                    // Long press detected, call voice mail
+                    callVoicemail();
+                }
+                return true;
+            }
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_CALL: {
+                if (mIsAddCallMode && (TextUtils.isEmpty(mDigits.getText().toString()))) {
+                    // if we are adding a call from the InCallScreen and the phone
+                    // number entered is empty, we just close the dialer to expose
+                    // the InCallScreen under it.
+                    finish();
+                } else {
+                    // otherwise, we place the call.
+                    placeCall();
+                }
+                return true;
+            }
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+    
+    private void keyPressed(int keyCode) {
+        KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
+        mDigits.onKeyDown(keyCode, event);
+    }
+
+    public boolean onKey(View view, int keyCode, KeyEvent event) {
+        switch (view.getId()) {
+            case R.id.digits:
+                if (keyCode == KeyEvent.KEYCODE_ENTER) {
+                    placeCall();
+                    return true;
+                }
+                break;
+        }
+        return false;
+    }
+
+    public void onClick(View view) {
+        switch (view.getId()) {
+            case R.id.one: {
+                playTone(ToneGenerator.TONE_DTMF_1);
+                keyPressed(KeyEvent.KEYCODE_1);
+                return;
+            }
+            case R.id.two: {
+                playTone(ToneGenerator.TONE_DTMF_2);
+                keyPressed(KeyEvent.KEYCODE_2);
+                return;
+            }
+            case R.id.three: {
+                playTone(ToneGenerator.TONE_DTMF_3);
+                keyPressed(KeyEvent.KEYCODE_3);
+                return;
+            }
+            case R.id.four: {
+                playTone(ToneGenerator.TONE_DTMF_4);
+                keyPressed(KeyEvent.KEYCODE_4);
+                return;
+            }
+            case R.id.five: {
+                playTone(ToneGenerator.TONE_DTMF_5);
+                keyPressed(KeyEvent.KEYCODE_5);
+                return;
+            }
+            case R.id.six: {
+                playTone(ToneGenerator.TONE_DTMF_6);
+                keyPressed(KeyEvent.KEYCODE_6);
+                return;
+            }
+            case R.id.seven: {
+                playTone(ToneGenerator.TONE_DTMF_7);
+                keyPressed(KeyEvent.KEYCODE_7);
+                return;
+            }
+            case R.id.eight: {
+                playTone(ToneGenerator.TONE_DTMF_8);
+                keyPressed(KeyEvent.KEYCODE_8);
+                return;
+            }
+            case R.id.nine: {
+                playTone(ToneGenerator.TONE_DTMF_9);
+                keyPressed(KeyEvent.KEYCODE_9);
+                return;
+            }
+            case R.id.zero: {
+                playTone(ToneGenerator.TONE_DTMF_0);
+                keyPressed(KeyEvent.KEYCODE_0);
+                return;
+            }
+            case R.id.pound: {
+                playTone(ToneGenerator.TONE_DTMF_P);
+                keyPressed(KeyEvent.KEYCODE_POUND);
+                return;
+            }
+            case R.id.star: {
+                playTone(ToneGenerator.TONE_DTMF_S);
+                keyPressed(KeyEvent.KEYCODE_STAR);
+                return;
+            }
+            case R.id.backspace: {
+                keyPressed(KeyEvent.KEYCODE_DEL);
+                return;
+            }
+            case R.id.digits: {
+                placeCall();
+                return;
+            }
+        }
+    }
+
+    public boolean onLongClick(View view) {
+        final Editable digits = mDigits.getText();
+        int id = view.getId();
+        switch (id) {
+            case R.id.backspace: {
+                digits.clear();
+                return true;
+            }
+            case R.id.one: {
+                if (digits.length() == 0) {
+                    callVoicemail();
+                    return true;
+                }
+                return false;
+            }
+            case R.id.zero: {
+                keyPressed(KeyEvent.KEYCODE_PLUS);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void callVoicemail() {
+        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+                Uri.fromParts("voicemail", "", null));
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+        mDigits.getText().clear();
+        finish();
+    }
+
+    void placeCall() {
+        final String number = mDigits.getText().toString();
+        if (number == null || !TextUtils.isGraphic(number)) {
+            // There is no number entered.
+            playTone(ToneGenerator.TONE_PROP_NACK);
+            return;
+        }
+        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+                Uri.fromParts("tel", number, null));
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+        mDigits.getText().clear();
+        finish();
+    }
+
+    Handler mToneStopper = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case STOP_TONE:
+                    synchronized(mToneGeneratorLock) {
+                        if (mToneGenerator == null) {
+                            Log.w(TAG, "mToneStopper: mToneGenerator == null");
+                        } else {
+                            mToneGenerator.stopTone();
+                        }
+                    }
+                    break;
+            }
+        }
+    };
+
+    /**
+     * Play a tone for TONE_LENGTH_MS milliseconds.
+     * 
+     * @param tone a tone code from {@link ToneGenerator}
+     */
+    void playTone(int tone) {
+        // if local tone playback is disabled, just return.
+        if (!mDTMFToneEnabled) {
+            return;
+        }
+ 
+        synchronized(mToneGeneratorLock) {
+            if (mToneGenerator == null) {
+                Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);
+                return;
+            }
+            
+            // Remove pending STOP_TONE messages
+            mToneStopper.removeMessages(STOP_TONE);
+    
+            // Start the new tone (will stop any playing tone)
+            mToneGenerator.startTone(tone);
+            mToneStopper.sendEmptyMessageDelayed(STOP_TONE, TONE_LENGTH_MS);
+        }
+    }
+}
+
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
new file mode 100644
index 0000000..c1e9bc5
--- /dev/null
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -0,0 +1,955 @@
+/*
+ * Copyright (C) 2007 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.android.contacts;
+
+import static com.android.contacts.ContactEntryAdapter.CONTACT_CUSTOM_RINGTONE_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.CONTACT_NAME_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.CONTACT_NOTES_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.CONTACT_PROJECTION;
+import static com.android.contacts.ContactEntryAdapter.CONTACT_SEND_TO_VOICEMAIL_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.CONTACT_STARRED_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_AUX_DATA_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_DATA_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_ID_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_KIND_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_LABEL_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_STATUS_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_TYPE_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.METHODS_WITH_PRESENCE_PROJECTION;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_COMPANY_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_ID_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_LABEL_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_PROJECTION;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_TITLE_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.ORGANIZATIONS_TYPE_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.PHONES_ID_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.PHONES_ISPRIMARY_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.PHONES_LABEL_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.PHONES_NUMBER_COLUMN;
+import static com.android.contacts.ContactEntryAdapter.PHONES_PROJECTION;
+import static com.android.contacts.ContactEntryAdapter.PHONES_TYPE_COLUMN;
+
+import com.android.internal.telephony.ITelephony;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.provider.Contacts;
+import android.provider.Im;
+import android.provider.Contacts.ContactMethods;
+import android.provider.Contacts.Organizations;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.Contacts.Presence;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+
+/**
+ * Displays the details of a specific contact.
+ */
+public class ViewContactActivity extends ListActivity 
+        implements View.OnCreateContextMenuListener, View.OnClickListener,
+        DialogInterface.OnClickListener {
+    private static final String TAG = "ViewContact";
+
+    private static final int DIALOG_CONFIRM_DELETE = 1;
+
+    public static final int MENU_ITEM_DELETE = 1;
+    public static final int MENU_ITEM_MAKE_DEFAULT = 2;
+
+    private Uri mUri;
+    private ContentResolver mResolver;
+    private ViewAdapter mAdapter;
+    private int mNumPhoneNumbers = 0;
+
+    /* package */ ArrayList<ViewEntry> mPhoneEntries = new ArrayList<ViewEntry>();
+    /* package */ ArrayList<ViewEntry> mSmsEntries = new ArrayList<ViewEntry>();
+    /* package */ ArrayList<ViewEntry> mEmailEntries = new ArrayList<ViewEntry>();
+    /* package */ ArrayList<ViewEntry> mPostalEntries = new ArrayList<ViewEntry>();
+    /* package */ ArrayList<ViewEntry> mImEntries = new ArrayList<ViewEntry>();
+    /* package */ ArrayList<ViewEntry> mOrganizationEntries = new ArrayList<ViewEntry>();
+    /* package */ ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
+    /* package */ ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
+
+    private Cursor mCursor;
+    private boolean mObserverRegistered;
+    
+    private ContentObserver mObserver = new ContentObserver(new Handler()) {
+        @Override
+        public boolean deliverSelfNotifications() {
+            return true;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (mCursor != null && !mCursor.isClosed()){
+                dataChanged();
+            }
+        }
+    };
+
+    public void onClick(DialogInterface dialog, int which) {
+        if (mCursor != null) {
+            if (mObserverRegistered) {
+                mCursor.unregisterContentObserver(mObserver);
+                mObserverRegistered = false;
+            }
+            mCursor.close();
+            mCursor = null;
+        }
+        getContentResolver().delete(mUri, null, null);
+        finish();
+    }
+
+    public void onClick(View view) {
+        if (!mObserverRegistered) {
+            return;
+        }
+        switch (view.getId()) {
+            case R.id.star: {
+                int oldStarredState = mCursor.getInt(CONTACT_STARRED_COLUMN);
+                ContentValues values = new ContentValues(1);
+                values.put(People.STARRED, oldStarredState == 1 ? 0 : 1);
+                getContentResolver().update(mUri, values, null, null);
+                break;
+            }
+        }
+    }
+
+    private TextView mNameView;
+    private ImageView mPhotoView;
+    private int mNoPhotoResource;
+    private CheckBox mStarView;
+    private boolean mShowSmsLinksForAllPhones;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        setContentView(R.layout.view_contact);
+        getListView().setOnCreateContextMenuListener(this);
+
+        mNameView = (TextView) findViewById(R.id.name);
+        mPhotoView = (ImageView) findViewById(R.id.photo);
+        mStarView = (CheckBox) findViewById(R.id.star);
+        mStarView.setOnClickListener(this);
+
+        // Set the photo with a random "no contact" image
+        long now = SystemClock.elapsedRealtime();
+        int num = (int) now & 0xf;
+        if (num < 9) {
+            // Leaning in from right, common
+            mNoPhotoResource = R.drawable.ic_contact_picture;
+        } else if (num < 14) {
+            // Leaning in from left uncommon
+            mNoPhotoResource = R.drawable.ic_contact_picture_2;
+        } else {
+            // Coming in from the top, rare
+            mNoPhotoResource = R.drawable.ic_contact_picture_3;
+        }
+
+        mUri = getIntent().getData();
+        mResolver = getContentResolver();
+
+        // Build the list of sections. The order they're added to mSections dictates the
+        // order they are displayed in the list.
+        mSections.add(mPhoneEntries);
+        mSections.add(mSmsEntries);
+        mSections.add(mEmailEntries);
+        mSections.add(mImEntries);
+        mSections.add(mPostalEntries);
+        mSections.add(mOrganizationEntries);
+        mSections.add(mOtherEntries);
+
+        //TODO Read this value from a preference
+        mShowSmsLinksForAllPhones = true;
+
+        mCursor = mResolver.query(mUri, CONTACT_PROJECTION, null, null, null);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mObserverRegistered = true;
+        mCursor.registerContentObserver(mObserver);
+        dataChanged();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mCursor != null) {
+            if (mObserverRegistered) {
+                mObserverRegistered = false;
+                mCursor.unregisterContentObserver(mObserver);
+            }
+            mCursor.deactivate();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        if (mCursor != null) {
+            if (mObserverRegistered) {
+                mCursor.unregisterContentObserver(mObserver);
+                mObserverRegistered = false;
+            }
+            mCursor.close();
+        }
+    }
+
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        switch (id) {
+            case DIALOG_CONFIRM_DELETE:
+                return new AlertDialog.Builder(this)
+                        .setTitle(R.string.deleteConfirmation_title)
+                        .setIcon(android.R.drawable.ic_dialog_alert)
+                        .setMessage(R.string.deleteConfirmation)
+                        .setNegativeButton(R.string.noButton, null)
+                        .setPositiveButton(R.string.yesButton, this)
+                        .setCancelable(false)
+                        .create();
+        }
+        return null;
+    }
+    
+    private void dataChanged() {
+        mCursor.requery();
+        if (mCursor.moveToFirst()) {
+            // Set the name
+            String name = mCursor.getString(CONTACT_NAME_COLUMN);
+            if (TextUtils.isEmpty(name)) {
+                mNameView.setText(getText(android.R.string.unknownName));
+            } else {
+                mNameView.setText(name);
+            }
+
+            // Load the photo
+            mPhotoView.setImageBitmap(People.loadContactPhoto(this, mUri, mNoPhotoResource,
+                    null /* use the default options */));
+
+            // Set the star
+            mStarView.setChecked(mCursor.getInt(CONTACT_STARRED_COLUMN) == 1 ? true : false);
+
+            // Build up the contact entries
+            buildEntries(mCursor);
+            if (mAdapter == null) {
+                mAdapter = new ViewAdapter(this, mSections);
+                setListAdapter(mAdapter);
+            } else {
+                mAdapter.setSections(mSections, true);
+            }
+        } else {
+            Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
+            Log.e(TAG, "invalid contact uri: " + mUri);
+            finish();
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        menu.add(0, 0, 0, R.string.menu_editContact)
+                .setIcon(android.R.drawable.ic_menu_edit)
+                .setIntent(new Intent(Intent.ACTION_EDIT, mUri))
+                .setAlphabeticShortcut('e');
+        menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_deleteContact)
+                .setIcon(android.R.drawable.ic_menu_delete);
+
+        return true;
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+        AdapterView.AdapterContextMenuInfo info;
+        try {
+             info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+        } catch (ClassCastException e) {
+            Log.e(TAG, "bad menuInfo", e);
+            return;
+        }
+
+        // This can be null sometimes, don't crash...
+        if (info == null) {
+            Log.e(TAG, "bad menuInfo");
+            return;
+        }
+
+        ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position, true);
+        switch (entry.kind) {
+            case Contacts.KIND_PHONE: {
+                menu.add(0, 0, 0, R.string.menu_call).setIntent(entry.intent);
+                menu.add(0, 0, 0, R.string.menu_sendSMS).setIntent(entry.auxIntent);
+                if (entry.primaryIcon == -1) {
+                    menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultNumber);
+                }
+                break;
+            }
+
+            case Contacts.KIND_EMAIL: {
+                menu.add(0, 0, 0, R.string.menu_sendEmail).setIntent(entry.intent);
+                break;
+            }
+
+            case Contacts.KIND_POSTAL: {
+                menu.add(0, 0, 0, R.string.menu_viewAddress).setIntent(entry.intent);
+                break;
+            }
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_ITEM_DELETE: {
+                // Get confirmation
+                showDialog(DIALOG_CONFIRM_DELETE);
+                return true;
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+    
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_ITEM_MAKE_DEFAULT: {
+                AdapterView.AdapterContextMenuInfo info;
+                try {
+                     info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+                } catch (ClassCastException e) {
+                    Log.e(TAG, "bad menuInfo", e);
+                    break;
+                }
+
+                ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position, true);
+                ContentValues values = new ContentValues(1);
+                values.put(People.PRIMARY_PHONE_ID, entry.id);
+                getContentResolver().update(mUri, values, null, null);
+                dataChanged();
+                return true;
+            }
+        }
+        return super.onContextItemSelected(item);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_CALL: {
+                try {
+                    ITelephony phone = ITelephony.Stub.asInterface(
+                            ServiceManager.checkService("phone"));
+                    if (phone != null && !phone.isIdle()) {
+                        // Skip out and let the key be handled at a higher level
+                        break;
+                    }
+                } catch (RemoteException re) {
+                    // Fall through and try to call the contact
+                }
+
+                int index = getListView().getSelectedItemPosition();
+                if (index != -1) {
+                    ViewEntry entry = ViewAdapter.getEntry(mSections, index, true);
+                    if (entry.kind == Contacts.KIND_PHONE) {
+                        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, entry.uri);
+                        startActivity(intent);
+                    }
+                } else if (mNumPhoneNumbers != 0) {
+                    // There isn't anything selected, call the default number
+                    Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, mUri);
+                    startActivity(intent);
+                }
+                return true;
+            }
+
+            case KeyEvent.KEYCODE_DEL: {
+                showDialog(DIALOG_CONFIRM_DELETE);
+                return true;
+            }
+        }
+
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        ViewEntry entry = ViewAdapter.getEntry(mSections, position, true);
+        if (entry != null) {
+            Intent intent = entry.intent;
+            if (intent != null) {
+                try {
+                    startActivity(intent);
+                } catch (ActivityNotFoundException e) {
+                    Log.e(TAG, "No activity found for intent: " + intent);
+                    signalError();
+                }
+            } else {
+                signalError();
+            }
+        } else {
+            signalError();
+        }
+    }
+
+    /**
+     * Signal an error to the user via a beep, or some other method.
+     */
+    private void signalError() {
+        //TODO: implement this when we have the sonification APIs
+    }
+
+    /**
+     * Build separator entries for all of the sections.
+     */
+    private void buildSeparators() {
+        ViewEntry separator;
+        
+        separator = new ViewEntry();
+        separator.kind = ViewEntry.KIND_SEPARATOR;
+        separator.data = getString(R.string.listSeparatorCallNumber);
+        mPhoneEntries.add(separator);
+
+        separator = new ViewEntry();
+        separator.kind = ViewEntry.KIND_SEPARATOR;
+        separator.data = getString(R.string.listSeparatorSendSmsMms);
+        mSmsEntries.add(separator);
+
+        separator = new ViewEntry();
+        separator.kind = ViewEntry.KIND_SEPARATOR;
+        separator.data = getString(R.string.listSeparatorSendEmail);
+        mEmailEntries.add(separator);
+
+        separator = new ViewEntry();
+        separator.kind = ViewEntry.KIND_SEPARATOR;
+        separator.data = getString(R.string.listSeparatorSendIm);
+        mImEntries.add(separator);
+
+        separator = new ViewEntry();
+        separator.kind = ViewEntry.KIND_SEPARATOR;
+        separator.data = getString(R.string.listSeparatorMapAddress);
+        mPostalEntries.add(separator);
+
+        separator = new ViewEntry();
+        separator.kind = ViewEntry.KIND_SEPARATOR;
+        separator.data = getString(R.string.listSeparatorOrganizations);
+        mOrganizationEntries.add(separator);
+
+        separator = new ViewEntry();
+        separator.kind = ViewEntry.KIND_SEPARATOR;
+        separator.data = getString(R.string.listSeparatorOtherInformation);
+        mOtherEntries.add(separator);
+    }
+    
+    /**
+     * Build up the entries to display on the screen.
+     * 
+     * @param personCursor the URI for the contact being displayed
+     */
+    private final void buildEntries(Cursor personCursor) {
+        // Clear out the old entries
+        final int numSections = mSections.size();
+        for (int i = 0; i < numSections; i++) {
+            mSections.get(i).clear();
+        }
+        buildSeparators();
+
+        // Build up the phone entries
+        final Uri phonesUri = Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY);
+        final Cursor phonesCursor = mResolver.query(phonesUri, PHONES_PROJECTION, null, null,
+                Phones.ISPRIMARY + " DESC");
+
+        if (phonesCursor != null) {
+            while (phonesCursor.moveToNext()) {
+                final int type = phonesCursor.getInt(PHONES_TYPE_COLUMN);
+                final String number = phonesCursor.getString(PHONES_NUMBER_COLUMN);
+                final String label = phonesCursor.getString(PHONES_LABEL_COLUMN);
+                final boolean isPrimary = phonesCursor.getInt(PHONES_ISPRIMARY_COLUMN) == 1;
+                final long id = phonesCursor.getLong(PHONES_ID_COLUMN);
+                final Uri uri = ContentUris.withAppendedId(phonesUri, id);
+
+                // Don't crash if the number is bogus
+                if (TextUtils.isEmpty(number)) {
+                    Log.w(TAG, "empty number for phone " + id);
+                    continue;
+                }
+
+                mNumPhoneNumbers++;
+                
+                // Add a phone number entry
+                final ViewEntry entry = new ViewEntry();
+                entry.label = Phones.getDisplayLabel(this, type, label).toString();
+                entry.data = number;
+                entry.id = id;
+                entry.uri = uri;
+                entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, entry.uri);
+                entry.auxIntent = new Intent(Intent.ACTION_SENDTO,
+                        Uri.fromParts("sms", number, null));
+                entry.kind = Contacts.KIND_PHONE;
+                if (isPrimary) {
+                    entry.primaryIcon = R.drawable.ic_default_number;
+                }
+                entry.actionIcon = R.drawable.sym_action_call;
+                mPhoneEntries.add(entry);
+
+                if (type == Phones.TYPE_MOBILE || mShowSmsLinksForAllPhones) {
+                    // Add an SMS entry
+                    ViewEntry smsEntry = new ViewEntry();
+                    smsEntry.label = entry.label;
+                    smsEntry.data = number;
+                    smsEntry.id = id;
+                    smsEntry.uri = uri;
+                    smsEntry.intent = entry.auxIntent;
+                    smsEntry.kind = ViewEntry.KIND_SMS;
+                    smsEntry.actionIcon = R.drawable.sym_action_sms;
+                    mSmsEntries.add(smsEntry);
+                }
+            }
+
+            phonesCursor.close();
+        }
+
+        // Build the contact method entries
+        final Uri methodsUri = Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY);
+        Cursor methodsCursor = mResolver.query(
+                Uri.withAppendedPath(mUri, "contact_methods_with_presence"),
+                METHODS_WITH_PRESENCE_PROJECTION, null, null, null);
+
+        if (methodsCursor != null) {
+            String[] protocolStrings = getResources().getStringArray(android.R.array.imProtocols);
+
+            while (methodsCursor.moveToNext()) {
+                final int kind = methodsCursor.getInt(METHODS_KIND_COLUMN);
+                final String label = methodsCursor.getString(METHODS_LABEL_COLUMN);
+                final String data = methodsCursor.getString(METHODS_DATA_COLUMN);
+                final int type = methodsCursor.getInt(METHODS_TYPE_COLUMN);
+                final long id = methodsCursor.getLong(METHODS_ID_COLUMN);
+                final Uri uri = ContentUris.withAppendedId(methodsUri, id);
+
+                // Don't crash if the data is bogus
+                if (TextUtils.isEmpty(data)) {
+                    Log.w(TAG, "empty data for contact method " + id);
+                    continue;
+                }
+
+                ViewEntry entry = new ViewEntry();
+                entry.id = id;
+                entry.uri = uri;
+                entry.kind = kind;
+
+                switch (kind) {
+                    case Contacts.KIND_EMAIL:
+                        entry.label = ContactMethods.getDisplayLabel(this, kind, type, label)
+                                .toString();
+                        entry.data = data;
+                        entry.intent = new Intent(Intent.ACTION_SENDTO,
+                                Uri.fromParts("mailto", data, null));
+                        if (!methodsCursor.isNull(METHODS_STATUS_COLUMN)) {
+                            entry.presenceIcon = Presence.getPresenceIconResourceId(
+                                    methodsCursor.getInt(METHODS_STATUS_COLUMN));
+                        }
+                        entry.actionIcon = R.drawable.sym_action_email;
+                        mEmailEntries.add(entry);
+                        break;
+
+                    case Contacts.KIND_POSTAL:
+                        entry.label = ContactMethods.getDisplayLabel(this, kind, type, label)
+                                .toString();
+                        entry.data = data;
+                        entry.maxLines = 4;
+                        entry.intent = new Intent(Intent.ACTION_VIEW, uri);
+                        entry.actionIcon = R.drawable.sym_action_map;
+                        mPostalEntries.add(entry);
+                        break;
+
+                    case Contacts.KIND_IM: {
+                        Object protocolObj = ContactMethods.decodeImProtocol(
+                                methodsCursor.getString(METHODS_AUX_DATA_COLUMN));
+                        String providerCategory;
+                        if (protocolObj instanceof Number) {
+                            int protocol = ((Number) protocolObj).intValue();
+                            entry.label = protocolStrings[protocol];
+                            providerCategory = ContactMethods.lookupProviderCategoryFromId(
+                                    protocol);
+                            if (protocol == ContactMethods.PROTOCOL_GOOGLE_TALK
+                                    || protocol == ContactMethods.PROTOCOL_MSN) {
+                                entry.maxLabelLines = 2;
+                            }
+                        } else {
+                            String providerName = (String) protocolObj;
+                            entry.label = providerName;
+                            providerCategory = Im.Provider.getProviderCategoryFromName(
+                                    providerName);
+                        }
+
+                        // Only add the intent if there is a valid provider name
+                        if (!TextUtils.isEmpty(providerCategory)) {
+                            entry.intent = new Intent(Intent.ACTION_SENDTO,
+                                    Uri.fromParts("im", data, null)).addCategory(providerCategory);
+                        }
+                        entry.data = data;
+                        if (!methodsCursor.isNull(METHODS_STATUS_COLUMN)) {
+                            entry.presenceIcon = Presence.getPresenceIconResourceId(
+                                    methodsCursor.getInt(METHODS_STATUS_COLUMN));
+                        }
+                        entry.actionIcon = R.drawable.sym_action_chat;
+                        mImEntries.add(entry);
+                        break;
+                    }
+                }
+            }
+
+            methodsCursor.close();
+        }
+
+        // Build IM entries for things we have presence info about but not explicit IM entries for
+        long personId = ContentUris.parseId(mUri);
+        String[] projection = new String[] {
+                Presence.IM_HANDLE, // 0
+                Presence.IM_PROTOCOL, // 1
+                Presence.PRESENCE_STATUS, // 2
+        };
+        Cursor presenceCursor = mResolver.query(Presence.CONTENT_URI, projection,
+                Presence.PERSON_ID + "=" + personId, null, null);
+        if (presenceCursor != null) {
+            try {
+                while (presenceCursor.moveToNext()) {
+                    // Find the display info for the provider
+                    String data = presenceCursor.getString(0);
+                    String label;
+                    Object protocolObj = ContactMethods.decodeImProtocol(
+                            presenceCursor.getString(1));
+                    String providerCategory;
+                    if (protocolObj instanceof Number) {
+                        int protocol = ((Number) protocolObj).intValue();
+                        label = getResources().getStringArray(
+                                android.R.array.imProtocols)[protocol];
+                        providerCategory = ContactMethods.lookupProviderCategoryFromId(
+                                protocol);
+                    } else {
+                        String providerName = (String) protocolObj;
+                        label = providerName;
+                        providerCategory = Im.Provider.getProviderCategoryFromName(providerName);
+                    }
+
+                    if (TextUtils.isEmpty(providerCategory)) {
+                        // A valid provider name is required
+                        continue;
+                    }
+
+                    Intent intent = new Intent(Intent.ACTION_SENDTO,
+                            Uri.fromParts("im", data, null)).addCategory(providerCategory);
+
+                    // Check to see if there is already an entry for this IM account
+                    boolean addEntry = true;
+                    int numImEntries = mImEntries.size();
+                    for (int i = 0; i < numImEntries; i++) {
+                        // Check to see if the intent point to the same thing, if so we won't
+                        // add this entry to the list since there is already an explict entry
+                        // for the IM account
+                        Intent existingIntent = mImEntries.get(i).intent;
+                        if (intent.filterEquals(existingIntent)) {
+                            addEntry = false;
+                            break;
+                        }
+                    }
+
+                    // Add the entry if an existing one wasn't found
+                    if (addEntry) {
+                        ViewEntry entry = new ViewEntry();
+                        entry.kind = Contacts.KIND_IM;
+                        entry.data = data;
+                        entry.label = label;
+                        entry.intent = intent;
+                        entry.actionIcon = R.drawable.sym_action_chat;
+                        entry.presenceIcon = Presence.getPresenceIconResourceId(
+                                presenceCursor.getInt(2));
+                        entry.maxLabelLines = 2;
+                        mImEntries.add(entry);
+                    }
+                }
+            } finally {
+                presenceCursor.close();
+            }
+        }
+
+        // Build the organization entries
+        final Uri organizationsUri = Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY);
+        Cursor organizationsCursor = mResolver.query(organizationsUri, ORGANIZATIONS_PROJECTION,
+                null, null, null);
+
+        if (organizationsCursor != null) {
+            while (organizationsCursor.moveToNext()) {
+                ViewEntry entry = new ViewEntry();
+                entry.id = organizationsCursor.getLong(ORGANIZATIONS_ID_COLUMN);
+                entry.uri = ContentUris.withAppendedId(organizationsUri, entry.id);
+                entry.kind = Contacts.KIND_ORGANIZATION;
+                entry.data = organizationsCursor.getString(ORGANIZATIONS_COMPANY_COLUMN);
+                entry.data2 = organizationsCursor.getString(ORGANIZATIONS_TITLE_COLUMN);
+                entry.label = Organizations.getDisplayLabel(this,
+                        organizationsCursor.getInt(ORGANIZATIONS_TYPE_COLUMN),
+                        organizationsCursor.getString(ORGANIZATIONS_LABEL_COLUMN)).toString();
+                mOrganizationEntries.add(entry);
+            }
+
+            organizationsCursor.close();
+        }
+
+
+        // Build the other entries
+        String note = personCursor.getString(CONTACT_NOTES_COLUMN);
+        if (!TextUtils.isEmpty(note)) {
+            ViewEntry entry = new ViewEntry();
+            entry.label = getString(R.string.label_notes);
+            entry.data = note;
+            entry.id = 0;
+            entry.kind = ViewEntry.KIND_CONTACT;
+            entry.uri = null;
+            entry.intent = null;
+            entry.maxLines = 10;
+            mOtherEntries.add(entry);
+        }
+        
+        // Build the ringtone entry
+        String ringtoneStr = personCursor.getString(CONTACT_CUSTOM_RINGTONE_COLUMN);
+        if (!TextUtils.isEmpty(ringtoneStr)) {
+            // Get the URI
+            Uri ringtoneUri = Uri.parse(ringtoneStr);
+            if (ringtoneUri != null) {
+                Ringtone ringtone = RingtoneManager.getRingtone(this, ringtoneUri);
+                if (ringtone != null) {
+                    ViewEntry entry = new ViewEntry();
+                    entry.label = getString(R.string.label_ringtone);
+                    entry.data = ringtone.getTitle(this);
+                    entry.kind = ViewEntry.KIND_CONTACT;
+                    entry.uri = ringtoneUri;
+                    mOtherEntries.add(entry);
+                }
+            }
+        }
+
+        boolean sendToVoicemail = personCursor.getInt(CONTACT_SEND_TO_VOICEMAIL_COLUMN) == 1;
+        if (sendToVoicemail) {
+            ViewEntry entry = new ViewEntry();
+            entry.label = getString(R.string.send_to_voicemail_view);
+            entry.isLabelOnly = true;
+            entry.kind = ViewEntry.KIND_CONTACT;
+            mOtherEntries.add(entry);
+        }
+    }
+
+    /**
+     * A basic structure with the data for a contact entry in the list.
+     */
+    private final static class ViewEntry extends ContactEntryAdapter.Entry {
+        public int primaryIcon = -1;
+        public Intent intent;
+        public Intent auxIntent = null;
+        public int presenceIcon = -1;
+        public int actionIcon = -1;
+        public String data2 = null;
+        public boolean isLabelOnly = false;
+        public int maxLabelLines = 1;
+    }
+
+    private static final class ViewAdapter extends ContactEntryAdapter<ViewEntry> {
+        /** Cache of the children views of a row */
+        static class ViewCache {
+            public TextView label;
+            public TextView data;
+            public TextView data2;
+            
+            // Need to keep track of this too
+            ViewEntry entry;
+        }
+        
+        ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
+            super(context, sections, true);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            ViewEntry entry = getEntry(mSections, position, true); 
+            View v;
+
+            // Handle separators specially
+            if (entry.kind == ViewEntry.KIND_SEPARATOR) {
+                TextView separator = (TextView) mInflater.inflate(
+                        R.layout.list_separator, parent, false);
+                separator.setText(entry.data);
+                return separator;
+            }
+
+            ViewCache views;
+
+            // Check to see if we can reuse convertView
+            if (convertView != null) {
+                views = (ViewCache) convertView.getTag();
+                if (views != null) {
+                    ViewEntry origEntry = views.entry;
+                    if (origEntry != null) {
+                        // Check to see if the view and the entry are compatible
+                        if (entry.kind == Contacts.KIND_ORGANIZATION
+                                && origEntry.kind != Contacts.KIND_ORGANIZATION) {
+                            v = null;
+                        } else if (entry.kind != Contacts.KIND_ORGANIZATION
+                                && origEntry.kind == Contacts.KIND_ORGANIZATION) {
+                            v = null;
+                        } else if (entry.isLabelOnly != origEntry.isLabelOnly) {
+                            v = null;
+                        } else {
+                            v = convertView;
+                        }
+                    } else {
+                        v = null;
+                    }
+                } else {
+                    v = null;
+                }
+            } else {
+                v = null;
+            }
+
+            // Create a new view if needed
+            if (v == null) {
+                if (entry.kind == Contacts.KIND_ORGANIZATION) {
+                    v = mInflater.inflate(R.layout.view_contact_entry_organization, parent, false);
+                } else if (entry.isLabelOnly) {
+                    v = mInflater.inflate(R.layout.view_contact_entry_only_label, parent, false);
+                } else {
+                    v = mInflater.inflate(R.layout.view_contact_entry, parent, false);
+                }
+
+                // Cache the children
+                views = new ViewCache();
+                views.label = (TextView) v.findViewById(R.id.label);
+                views.data = (TextView) v.findViewById(R.id.data);
+                // label-only contact entries don't have a data view
+                if (views.data != null) {
+                    views.data.setCompoundDrawablePadding(3);
+                }
+                views.data2 = (TextView) v.findViewById(R.id.data2);
+            } else {
+                views = (ViewCache) v.getTag();
+            }
+
+            // Set the tag on the view so it knows what it's displaying
+            views.entry = entry;
+            v.setTag(views);
+
+            // Bind the data to the view
+            bindView(v, entry);
+            return v;
+        }
+
+        @Override
+        protected View newView(int position, ViewGroup parent) {
+            // getView() handles this
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected void bindView(View view, ViewEntry entry) {
+            final Resources resources = mContext.getResources();
+            ViewCache views = (ViewCache) view.getTag();
+
+            // Set the label
+            TextView label = views.label;
+            setMaxLines(label, entry.maxLabelLines);
+            label.setText(entry.label);
+
+            // Set the data
+            TextView data = views.data;
+            if (data != null) {
+                data.setText(entry.data);
+                setMaxLines(data, entry.maxLines);
+            }
+
+            // Set the left icon
+            Drawable left = null;
+            if (entry.primaryIcon != -1) {
+                left = resources.getDrawable(entry.primaryIcon);
+            } else if (entry.presenceIcon != -1) {
+                left = resources.getDrawable(entry.presenceIcon);
+            }
+
+            // Set the right icon
+            Drawable right = null;
+            if (entry.actionIcon != -1) {
+                right = resources.getDrawable(entry.actionIcon);
+            }
+            
+            if (data != null) {
+                data.setCompoundDrawablesWithIntrinsicBounds(left, null, right, null);
+            }
+
+            // Set data2 if we have it
+            if (entry.kind == Contacts.KIND_ORGANIZATION) {
+                views.data2.setText(entry.data2);
+            }
+        }
+
+        private void setMaxLines(TextView textView, int maxLines) {
+            if (maxLines == 1) {
+                textView.setSingleLine(true);
+                textView.setEllipsize(TextUtils.TruncateAt.END);
+            } else {
+                textView.setSingleLine(false);
+                textView.setMaxLines(maxLines);
+                textView.setEllipsize(null);
+            }
+        }
+    }
+}
+
+
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..47782e3
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := ContactsTests
+
+LOCAL_INSTRUMENTATION_FOR := Contacts
+
+include $(BUILD_PACKAGE)
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
new file mode 100644
index 0000000..ca28a6a
--- /dev/null
+++ b/tests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.android.contacts.tests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="ContactsLaunchPerformance"
+        android:targetPackage="com.android.contacts"
+        android:label="Contacts Launch Performance">
+    </instrumentation>
+
+</manifest> 
diff --git a/tests/src/com/android/contacts/ContactsLaunchPerformance.java b/tests/src/com/android/contacts/ContactsLaunchPerformance.java
new file mode 100644
index 0000000..85dba56
--- /dev/null
+++ b/tests/src/com/android/contacts/ContactsLaunchPerformance.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007 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.android.contacts.tests;
+
+import android.app.Activity;
+import android.test.LaunchPerformanceBase;
+import android.os.Bundle;
+
+import java.util.Map;
+
+/**
+ * Instrumentation class for Address Book launch performance testing.
+ */
+public class ContactsLaunchPerformance extends LaunchPerformanceBase {
+
+    public static final String LOG_TAG = "ContactsLaunchPerformance";
+
+    public ContactsLaunchPerformance() {
+        super();
+    }
+
+    @Override
+    public void onCreate(Bundle arguments) {
+        mIntent.setClassName(getTargetContext(), "com.android.contacts.ContactsListActivity");
+
+        start();
+    }
+
+    /**
+     * Calls LaunchApp and finish.
+     */
+    @Override
+    public void onStart() {
+        super.onStart();
+        LaunchApp();
+        finish(Activity.RESULT_OK, mResults);
+    }
+}