summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk18
-rw-r--r--AndroidManifest.xml326
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--NOTICE190
-rw-r--r--res/color-finger/dialer_button_text.xml24
-rw-r--r--res/drawable-finger/btn_circle.xml32
-rw-r--r--res/drawable-finger/btn_circle_disable.pngbin0 -> 938 bytes
-rw-r--r--res/drawable-finger/btn_circle_disable_focused.pngbin0 -> 1436 bytes
-rw-r--r--res/drawable-finger/btn_circle_longpress.pngbin0 -> 1590 bytes
-rw-r--r--res/drawable-finger/btn_circle_normal.pngbin0 -> 1249 bytes
-rw-r--r--res/drawable-finger/btn_circle_pressed.pngbin0 -> 1613 bytes
-rw-r--r--res/drawable-finger/btn_circle_selected.pngbin0 -> 1645 bytes
-rw-r--r--res/drawable-finger/btn_contact_picture.xml26
-rw-r--r--res/drawable-finger/btn_dial.xml24
-rw-r--r--res/drawable-finger/btn_dial_delete.xml25
-rw-r--r--res/drawable-finger/btn_dial_delete_activated.9.pngbin0 -> 960 bytes
-rw-r--r--res/drawable-finger/btn_dial_delete_active.xml25
-rw-r--r--res/drawable-finger/btn_dial_delete_normal.9.pngbin0 -> 1348 bytes
-rw-r--r--res/drawable-finger/btn_dial_delete_pressed.9.pngbin0 -> 1720 bytes
-rw-r--r--res/drawable-finger/btn_dial_delete_selected.9.pngbin0 -> 1815 bytes
-rw-r--r--res/drawable-finger/btn_dial_normal.pngbin0 -> 4104 bytes
-rw-r--r--res/drawable-finger/btn_dial_pressed.pngbin0 -> 4200 bytes
-rw-r--r--res/drawable-finger/btn_dial_selected.pngbin0 -> 4280 bytes
-rw-r--r--res/drawable-finger/btn_dial_textfield.xml25
-rw-r--r--res/drawable-finger/btn_dial_textfield_activated.9.pngbin0 -> 993 bytes
-rw-r--r--res/drawable-finger/btn_dial_textfield_active.xml25
-rw-r--r--res/drawable-finger/btn_dial_textfield_normal.9.pngbin0 -> 1438 bytes
-rw-r--r--res/drawable-finger/btn_dial_textfield_pressed.9.pngbin0 -> 1760 bytes
-rw-r--r--res/drawable-finger/btn_dial_textfield_selected.9.pngbin0 -> 1867 bytes
-rw-r--r--res/drawable-finger/contact_picture_border_highlight.9.pngbin0 -> 838 bytes
-rw-r--r--res/drawable-finger/contact_picture_border_longpress.9.pngbin0 -> 828 bytes
-rw-r--r--res/drawable-finger/contact_picture_border_normal.9.pngbin0 -> 453 bytes
-rw-r--r--res/drawable-finger/contact_picture_border_pressed.9.pngbin0 -> 849 bytes
-rw-r--r--res/drawable-finger/dial_num_0.xml25
-rw-r--r--res/drawable-finger/dial_num_0_blk.pngbin0 -> 474 bytes
-rw-r--r--res/drawable-finger/dial_num_0_wht.pngbin0 -> 1272 bytes
-rw-r--r--res/drawable-finger/dial_num_1.xml25
-rw-r--r--res/drawable-finger/dial_num_1_blk.pngbin0 -> 577 bytes
-rw-r--r--res/drawable-finger/dial_num_1_wht.pngbin0 -> 1573 bytes
-rw-r--r--res/drawable-finger/dial_num_2.xml25
-rw-r--r--res/drawable-finger/dial_num_2_blk.pngbin0 -> 684 bytes
-rw-r--r--res/drawable-finger/dial_num_2_wht.pngbin0 -> 1821 bytes
-rw-r--r--res/drawable-finger/dial_num_3.xml25
-rw-r--r--res/drawable-finger/dial_num_3_blk.pngbin0 -> 578 bytes
-rw-r--r--res/drawable-finger/dial_num_3_wht.pngbin0 -> 1711 bytes
-rw-r--r--res/drawable-finger/dial_num_4.xml25
-rw-r--r--res/drawable-finger/dial_num_4_blk.pngbin0 -> 537 bytes
-rw-r--r--res/drawable-finger/dial_num_4_wht.pngbin0 -> 1638 bytes
-rw-r--r--res/drawable-finger/dial_num_5.xml25
-rw-r--r--res/drawable-finger/dial_num_5_blk.pngbin0 -> 533 bytes
-rw-r--r--res/drawable-finger/dial_num_5_wht.pngbin0 -> 1609 bytes
-rw-r--r--res/drawable-finger/dial_num_6.xml25
-rw-r--r--res/drawable-finger/dial_num_6_blk.pngbin0 -> 713 bytes
-rw-r--r--res/drawable-finger/dial_num_6_wht.pngbin0 -> 1971 bytes
-rw-r--r--res/drawable-finger/dial_num_7.xml25
-rw-r--r--res/drawable-finger/dial_num_7_blk.pngbin0 -> 692 bytes
-rw-r--r--res/drawable-finger/dial_num_7_wht.pngbin0 -> 2063 bytes
-rw-r--r--res/drawable-finger/dial_num_8.xml25
-rw-r--r--res/drawable-finger/dial_num_8_blk.pngbin0 -> 619 bytes
-rw-r--r--res/drawable-finger/dial_num_8_wht.pngbin0 -> 1766 bytes
-rw-r--r--res/drawable-finger/dial_num_9.xml25
-rw-r--r--res/drawable-finger/dial_num_9_blk.pngbin0 -> 801 bytes
-rw-r--r--res/drawable-finger/dial_num_9_wht.pngbin0 -> 2220 bytes
-rw-r--r--res/drawable-finger/dial_num_pound.xml25
-rw-r--r--res/drawable-finger/dial_num_pound_blk.pngbin0 -> 381 bytes
-rw-r--r--res/drawable-finger/dial_num_pound_wht.pngbin0 -> 1076 bytes
-rw-r--r--res/drawable-finger/dial_num_star.xml25
-rw-r--r--res/drawable-finger/dial_num_star_blk.pngbin0 -> 469 bytes
-rw-r--r--res/drawable-finger/dial_num_star_wht.pngbin0 -> 1133 bytes
-rw-r--r--res/drawable-finger/ic_btn_round_minus.pngbin0 -> 288 bytes
-rw-r--r--res/drawable-finger/ic_btn_round_more.pngbin0 -> 968 bytes
-rw-r--r--res/drawable-finger/ic_btn_round_plus.pngbin0 -> 526 bytes
-rw-r--r--res/drawable-finger/ic_contact_picture.pngbin0 -> 1719 bytes
-rw-r--r--res/drawable-finger/ic_contact_picture_2.pngbin0 -> 1894 bytes
-rw-r--r--res/drawable-finger/ic_contact_picture_3.pngbin0 -> 1364 bytes
-rw-r--r--res/drawable-finger/ic_default_number.pngbin0 -> 612 bytes
-rw-r--r--res/drawable-finger/ic_delete_phone_number.xml25
-rw-r--r--res/drawable-finger/ic_delete_phone_number_blk.pngbin0 -> 362 bytes
-rw-r--r--res/drawable-finger/ic_delete_phone_number_wht.pngbin0 -> 952 bytes
-rw-r--r--res/drawable-finger/ic_dial_number.xml25
-rw-r--r--res/drawable-finger/ic_dial_number_blk.pngbin0 -> 564 bytes
-rw-r--r--res/drawable-finger/ic_dial_number_wht.pngbin0 -> 1180 bytes
-rw-r--r--res/drawable-finger/ic_dialer_fork_add_call.pngbin0 -> 1309 bytes
-rw-r--r--res/drawable-finger/ic_dialer_fork_current_call.pngbin0 -> 1470 bytes
-rw-r--r--res/drawable-finger/ic_dialer_fork_tt_keypad.pngbin0 -> 1618 bytes
-rw-r--r--res/drawable-finger/ic_menu_add_picture.pngbin0 -> 1751 bytes
-rwxr-xr-xres/drawable-finger/ic_menu_import_contact.pngbin0 -> 1755 bytes
-rwxr-xr-xres/drawable-finger/ic_menu_show_barcode.pngbin0 -> 1602 bytes
-rw-r--r--res/drawable-finger/ic_tab_contacts.xml21
-rw-r--r--res/drawable-finger/ic_tab_dialer.xml21
-rw-r--r--res/drawable-finger/ic_tab_recent.xml21
-rw-r--r--res/drawable-finger/ic_tab_selected_contacts.pngbin0 -> 821 bytes
-rw-r--r--res/drawable-finger/ic_tab_selected_dialer.pngbin0 -> 1911 bytes
-rw-r--r--res/drawable-finger/ic_tab_selected_recent.pngbin0 -> 622 bytes
-rw-r--r--res/drawable-finger/ic_tab_selected_starred.pngbin0 -> 1227 bytes
-rw-r--r--res/drawable-finger/ic_tab_starred.xml21
-rw-r--r--res/drawable-finger/ic_tab_unselected_contacts.pngbin0 -> 573 bytes
-rw-r--r--res/drawable-finger/ic_tab_unselected_dialer.pngbin0 -> 1600 bytes
-rw-r--r--res/drawable-finger/ic_tab_unselected_recent.pngbin0 -> 558 bytes
-rw-r--r--res/drawable-finger/ic_tab_unselected_starred.pngbin0 -> 813 bytes
-rw-r--r--res/drawable-finger/star_on.pngbin0 -> 856 bytes
-rw-r--r--res/drawable-finger/sym_action_add.pngbin0 -> 930 bytes
-rw-r--r--res/drawable-finger/sym_action_edit.pngbin0 -> 761 bytes
-rw-r--r--res/drawable-finger/sym_action_map.pngbin0 -> 828 bytes
-rw-r--r--res/drawable-finger/sym_action_organization.pngbin0 -> 617 bytes
-rw-r--r--res/drawable-finger/sym_action_remove.pngbin0 -> 1034 bytes
-rw-r--r--res/drawable-finger/sym_action_search.pngbin0 -> 945 bytes
-rw-r--r--res/drawable-finger/sym_action_sms.pngbin0 -> 686 bytes
-rw-r--r--res/drawable-finger/sym_action_view_contact.pngbin0 -> 588 bytes
-rw-r--r--res/drawable-finger/sym_note.pngbin0 -> 418 bytes
-rw-r--r--res/drawable-finger/sym_ringtone.pngbin0 -> 766 bytes
-rw-r--r--res/drawable-finger/sym_send_to_voicemail.pngbin0 -> 1040 bytes
-rw-r--r--res/drawable-finger/title_bar_shadow.9.pngbin0 -> 178 bytes
-rw-r--r--res/drawable/ic_launcher_contacts.pngbin0 -> 2995 bytes
-rw-r--r--res/drawable/ic_launcher_contacts_phones.pngbin0 -> 3315 bytes
-rw-r--r--res/drawable/ic_launcher_contacts_starred.pngbin0 -> 3892 bytes
-rw-r--r--res/drawable/ic_launcher_phone.pngbin0 -> 3621 bytes
-rw-r--r--res/layout-finger/call_detail.xml89
-rw-r--r--res/layout-finger/call_detail_list_item.xml78
-rw-r--r--res/layout-finger/contacts_list_content.xml45
-rw-r--r--res/layout-finger/contacts_list_item.xml82
-rw-r--r--res/layout-finger/dialer_activity.xml42
-rw-r--r--res/layout-finger/dialpad.xml119
-rw-r--r--res/layout-finger/dialpad_chooser_list_item.xml35
-rw-r--r--res/layout-finger/edit_contact.xml117
-rw-r--r--res/layout-finger/edit_contact_entry.xml51
-rw-r--r--res/layout-finger/edit_contact_entry_org.xml61
-rw-r--r--res/layout-finger/edit_contact_entry_ringtone.xml68
-rw-r--r--res/layout-finger/edit_contact_entry_static_label.xml54
-rw-r--r--res/layout-finger/edit_contact_entry_voicemail.xml77
-rw-r--r--res/layout-finger/edit_divider.xml21
-rw-r--r--res/layout-finger/edit_phonetic_name.xml62
-rw-r--r--res/layout-finger/edit_separator.xml57
-rw-r--r--res/layout-finger/edit_separator_alone.xml48
-rw-r--r--res/layout-finger/list_item_text_icons.xml71
-rw-r--r--res/layout-finger/list_separator.xml27
-rw-r--r--res/layout-finger/recent_calls.xml34
-rw-r--r--res/layout-finger/recent_calls_list_item.xml70
-rw-r--r--res/layout-finger/sync_settings.xml53
-rw-r--r--res/layout-finger/twelve_key_dialer.xml71
-rw-r--r--res/layout-finger/view_contact.xml71
-rw-r--r--res/layout-finger/view_contact_entry.xml43
-rw-r--r--res/layout-finger/view_contact_entry_only_label.xml27
-rw-r--r--res/layout-finger/view_contact_entry_organization.xml63
-rw-r--r--res/layout-finger/view_contact_name.xml28
-rw-r--r--res/layout-ja-finger/edit_phonetic_name.xml46
-rw-r--r--res/layout-ja-finger/view_contact_name.xml41
-rw-r--r--res/layout-land-finger/twelve_key_dialer.xml72
-rw-r--r--res/values-cs/strings.xml172
-rw-r--r--res/values-de/strings.xml172
-rw-r--r--res/values-es/strings.xml165
-rw-r--r--res/values-fr/strings.xml165
-rw-r--r--res/values-it/strings.xml165
-rw-r--r--res/values-ja/strings.xml172
-rw-r--r--res/values-ko/strings.xml172
-rw-r--r--res/values-nb/strings.xml171
-rw-r--r--res/values-nl/strings.xml172
-rw-r--r--res/values-pl/strings.xml172
-rw-r--r--res/values-ru/strings.xml172
-rw-r--r--res/values-zh-rCN/strings.xml172
-rw-r--r--res/values-zh-rTW/strings.xml172
-rw-r--r--res/values/ids.xml20
-rw-r--r--res/values/strings.xml483
-rw-r--r--res/values/styles.xml34
-rw-r--r--res/xml/searchable.xml39
-rw-r--r--src/com/android/contacts/AttachImage.java106
-rw-r--r--src/com/android/contacts/ButtonGridLayout.java97
-rw-r--r--src/com/android/contacts/CallDetailActivity.java355
-rw-r--r--src/com/android/contacts/ContactEntryAdapter.java341
-rw-r--r--src/com/android/contacts/ContactsGroupSyncSelector.java256
-rw-r--r--src/com/android/contacts/ContactsListActivity.java1518
-rw-r--r--src/com/android/contacts/ContactsLiveFolders.java112
-rw-r--r--src/com/android/contacts/DialtactsActivity.java311
-rw-r--r--src/com/android/contacts/EditContactActivity.java2108
-rw-r--r--src/com/android/contacts/RecentCallsListActivity.java828
-rw-r--r--src/com/android/contacts/SpecialCharSequenceMgr.java310
-rw-r--r--src/com/android/contacts/TwelveKeyDialer.java913
-rw-r--r--src/com/android/contacts/ViewContactActivity.java1032
-rw-r--r--tests/Android.mk17
-rw-r--r--tests/AndroidManifest.xml29
-rw-r--r--tests/src/com/android/contacts/ContactsLaunchPerformance.java52
181 files changed, 14172 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 000000000..6a84c0cd1
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := user development
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := googlelogin-client
+
+LOCAL_PACKAGE_NAME := Contacts
+LOCAL_CERTIFICATE := shared
+
+LOCAL_STATIC_JAVA_LIBRARIES := googlelogin-client
+
+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 000000000..846551d7e
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,326 @@
+<?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" />
+ <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.mail" />
+
+ <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/launcherDialer"
+ android:theme="@android:style/Theme.NoTitleBar"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:icon="@drawable/ic_launcher_phone"
+ android:screenOrientation="nosensor"
+ >
+ <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/contactsList"
+ 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>
+
+ <activity android:name="CallDetailActivity"
+ android:label="@string/callDetailTitle"
+ android:theme="@android:style/Theme.NoTitleBar"
+ >
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.cursor.item/calls" />
+ </intent-filter>
+ </activity>
+
+ <!-- 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"
+ android:windowSoftInputMode="stateVisible|adjustResize">
+ <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>
+
+ <!-- Activity used to select the groups that should be synced -->
+ <activity android:name="ContactsGroupSyncSelector"
+ android:label="@string/seclectSyncGroups_title"
+ />
+
+ <!-- Makes .ContactsListActivity the search target for any activity in Contacts -->
+ <meta-data android:name="android.app.default_searchable"
+ android:value=".ContactsListActivity" />
+
+
+ <!-- LIVE FOLDERS -->
+ <activity
+ android:name=".ContactsLiveFolders$AllContacts"
+ android:label="@string/liveFolderAll"
+ android:icon="@drawable/ic_launcher_contacts">
+ <intent-filter>
+ <action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".ContactsLiveFolders$StarredContacts"
+ android:label="@string/liveFolderFavorites"
+ android:icon="@drawable/ic_launcher_contacts_starred">
+ <intent-filter>
+ <action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".ContactsLiveFolders$PhoneContacts"
+ android:label="@string/liveFolderPhone"
+ android:icon="@drawable/ic_launcher_contacts_phones">
+ <intent-filter>
+ <action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ </application>
+</manifest>
+
+
+
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 000000000..c5b1efa7a
--- /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 000000000..9e71988a2
--- /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/btn_circle.xml b/res/drawable-finger/btn_circle.xml
new file mode 100644
index 000000000..920801033
--- /dev/null
+++ b/res/drawable-finger/btn_circle.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_window_focused="false" android:state_enabled="true"
+ android:drawable="@drawable/btn_circle_normal" />
+ <item android:state_window_focused="false" android:state_enabled="false"
+ android:drawable="@drawable/btn_circle_disable" />
+ <item android:state_pressed="true"
+ android:drawable="@drawable/btn_circle_pressed" />
+ <item android:state_focused="true" android:state_enabled="true"
+ android:drawable="@drawable/btn_circle_selected" />
+ <item android:state_enabled="true"
+ android:drawable="@drawable/btn_circle_normal" />
+ <item android:state_focused="true"
+ android:drawable="@drawable/btn_circle_disable_focused" />
+ <item
+ android:drawable="@drawable/btn_circle_disable" />
+</selector>
diff --git a/res/drawable-finger/btn_circle_disable.png b/res/drawable-finger/btn_circle_disable.png
new file mode 100644
index 000000000..33b74a66c
--- /dev/null
+++ b/res/drawable-finger/btn_circle_disable.png
Binary files differ
diff --git a/res/drawable-finger/btn_circle_disable_focused.png b/res/drawable-finger/btn_circle_disable_focused.png
new file mode 100644
index 000000000..005ad8dca
--- /dev/null
+++ b/res/drawable-finger/btn_circle_disable_focused.png
Binary files differ
diff --git a/res/drawable-finger/btn_circle_longpress.png b/res/drawable-finger/btn_circle_longpress.png
new file mode 100644
index 000000000..f27d4119b
--- /dev/null
+++ b/res/drawable-finger/btn_circle_longpress.png
Binary files differ
diff --git a/res/drawable-finger/btn_circle_normal.png b/res/drawable-finger/btn_circle_normal.png
new file mode 100644
index 000000000..fc5af1c9f
--- /dev/null
+++ b/res/drawable-finger/btn_circle_normal.png
Binary files differ
diff --git a/res/drawable-finger/btn_circle_pressed.png b/res/drawable-finger/btn_circle_pressed.png
new file mode 100644
index 000000000..8f40afdfc
--- /dev/null
+++ b/res/drawable-finger/btn_circle_pressed.png
Binary files differ
diff --git a/res/drawable-finger/btn_circle_selected.png b/res/drawable-finger/btn_circle_selected.png
new file mode 100644
index 000000000..c74fac227
--- /dev/null
+++ b/res/drawable-finger/btn_circle_selected.png
Binary files differ
diff --git a/res/drawable-finger/btn_contact_picture.xml b/res/drawable-finger/btn_contact_picture.xml
new file mode 100644
index 000000000..643231a84
--- /dev/null
+++ b/res/drawable-finger/btn_contact_picture.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_window_focused="false"
+ android:drawable="@drawable/contact_picture_border_normal" />
+ <item android:state_pressed="true"
+ android:drawable="@drawable/contact_picture_border_pressed" />
+ <item android:state_focused="true"
+ android:drawable="@drawable/contact_picture_border_highlight" />
+ <item
+ android:drawable="@drawable/contact_picture_border_normal" />
+</selector>
diff --git a/res/drawable-finger/btn_dial.xml b/res/drawable-finger/btn_dial.xml
new file mode 100644
index 000000000..4dfcd642b
--- /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 000000000..235554dc7
--- /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 000000000..453a36845
--- /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 000000000..3403f34d4
--- /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 000000000..9da1cffe6
--- /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 000000000..5bbc20d44
--- /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 000000000..d959768a1
--- /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 000000000..ff8796b63
--- /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 000000000..3c198c405
--- /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 000000000..8ca2b0df6
--- /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 000000000..4eabf1856
--- /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 000000000..4c34576d9
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_activated.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 000000000..18b84c6db
--- /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 000000000..40ab86649
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_normal.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 000000000..0087327d9
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_pressed.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 000000000..63fceaa0a
--- /dev/null
+++ b/res/drawable-finger/btn_dial_textfield_selected.9.png
Binary files differ
diff --git a/res/drawable-finger/contact_picture_border_highlight.9.png b/res/drawable-finger/contact_picture_border_highlight.9.png
new file mode 100644
index 000000000..fc4e01db9
--- /dev/null
+++ b/res/drawable-finger/contact_picture_border_highlight.9.png
Binary files differ
diff --git a/res/drawable-finger/contact_picture_border_longpress.9.png b/res/drawable-finger/contact_picture_border_longpress.9.png
new file mode 100644
index 000000000..c89a55a12
--- /dev/null
+++ b/res/drawable-finger/contact_picture_border_longpress.9.png
Binary files differ
diff --git a/res/drawable-finger/contact_picture_border_normal.9.png b/res/drawable-finger/contact_picture_border_normal.9.png
new file mode 100644
index 000000000..37451ae99
--- /dev/null
+++ b/res/drawable-finger/contact_picture_border_normal.9.png
Binary files differ
diff --git a/res/drawable-finger/contact_picture_border_pressed.9.png b/res/drawable-finger/contact_picture_border_pressed.9.png
new file mode 100644
index 000000000..11eaae539
--- /dev/null
+++ b/res/drawable-finger/contact_picture_border_pressed.9.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_0.xml b/res/drawable-finger/dial_num_0.xml
new file mode 100644
index 000000000..cd4d727a2
--- /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 000000000..a10c601a2
--- /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 000000000..2de90a12a
--- /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 000000000..48737b24a
--- /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 000000000..822f9ca77
--- /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 000000000..e488bebe0
--- /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 000000000..1a087f696
--- /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 000000000..539c30115
--- /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 000000000..cd1ae8d21
--- /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 000000000..dda794a77
--- /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 000000000..dc70f3316
--- /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 000000000..dc7c8e23f
--- /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 000000000..511e58b59
--- /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 000000000..084db4445
--- /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 000000000..44128a103
--- /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 000000000..a857bc778
--- /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 000000000..0a06f2011
--- /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 000000000..fc605b82e
--- /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 000000000..b4944581e
--- /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 000000000..2498d77f2
--- /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 000000000..086e8ce6f
--- /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 000000000..a22c894ae
--- /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 000000000..cfb20a7f0
--- /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 000000000..cb1b0973a
--- /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 000000000..8ac79300e
--- /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 000000000..845ee6827
--- /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 000000000..1954f1061
--- /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 000000000..d0b7fd910
--- /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 000000000..752df25f2
--- /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 000000000..d8b5aa1e9
--- /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 000000000..609d748c1
--- /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 000000000..4dabda425
--- /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 000000000..f27ae87df
--- /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 000000000..3b3304a7f
--- /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 000000000..af917ef53
--- /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 000000000..ffbd43fd2
--- /dev/null
+++ b/res/drawable-finger/dial_num_star_wht.png
Binary files differ
diff --git a/res/drawable-finger/ic_btn_round_minus.png b/res/drawable-finger/ic_btn_round_minus.png
new file mode 100644
index 000000000..96dbb17d2
--- /dev/null
+++ b/res/drawable-finger/ic_btn_round_minus.png
Binary files differ
diff --git a/res/drawable-finger/ic_btn_round_more.png b/res/drawable-finger/ic_btn_round_more.png
new file mode 100644
index 000000000..ebdc55c27
--- /dev/null
+++ b/res/drawable-finger/ic_btn_round_more.png
Binary files differ
diff --git a/res/drawable-finger/ic_btn_round_plus.png b/res/drawable-finger/ic_btn_round_plus.png
new file mode 100644
index 000000000..1ec8a956a
--- /dev/null
+++ b/res/drawable-finger/ic_btn_round_plus.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 000000000..3a338e8e2
--- /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 000000000..8b184af7c
--- /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 000000000..a2d08b55f
--- /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 000000000..a27685e8c
--- /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 000000000..adfc0ced9
--- /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 000000000..2e83d365e
--- /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 000000000..988bcfd05
--- /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 000000000..1ba449e98
--- /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 000000000..c1f572dca
--- /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 000000000..d303b2bf5
--- /dev/null
+++ b/res/drawable-finger/ic_dial_number_wht.png
Binary files differ
diff --git a/res/drawable-finger/ic_dialer_fork_add_call.png b/res/drawable-finger/ic_dialer_fork_add_call.png
new file mode 100644
index 000000000..5a1555b17
--- /dev/null
+++ b/res/drawable-finger/ic_dialer_fork_add_call.png
Binary files differ
diff --git a/res/drawable-finger/ic_dialer_fork_current_call.png b/res/drawable-finger/ic_dialer_fork_current_call.png
new file mode 100644
index 000000000..1be783ef2
--- /dev/null
+++ b/res/drawable-finger/ic_dialer_fork_current_call.png
Binary files differ
diff --git a/res/drawable-finger/ic_dialer_fork_tt_keypad.png b/res/drawable-finger/ic_dialer_fork_tt_keypad.png
new file mode 100644
index 000000000..4e3dd8232
--- /dev/null
+++ b/res/drawable-finger/ic_dialer_fork_tt_keypad.png
Binary files differ
diff --git a/res/drawable-finger/ic_menu_add_picture.png b/res/drawable-finger/ic_menu_add_picture.png
new file mode 100644
index 000000000..35f10b8ed
--- /dev/null
+++ b/res/drawable-finger/ic_menu_add_picture.png
Binary files differ
diff --git a/res/drawable-finger/ic_menu_import_contact.png b/res/drawable-finger/ic_menu_import_contact.png
new file mode 100755
index 000000000..fcb16db01
--- /dev/null
+++ b/res/drawable-finger/ic_menu_import_contact.png
Binary files differ
diff --git a/res/drawable-finger/ic_menu_show_barcode.png b/res/drawable-finger/ic_menu_show_barcode.png
new file mode 100755
index 000000000..cb66ae53f
--- /dev/null
+++ b/res/drawable-finger/ic_menu_show_barcode.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 000000000..3341f41c0
--- /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 000000000..36115fa8f
--- /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 000000000..548d0b7e2
--- /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 000000000..16c4a91ab
--- /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 000000000..cb715ed09
--- /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 000000000..bdbfc417e
--- /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 000000000..1b884cb4c
--- /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 000000000..e84db4a44
--- /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 000000000..7b5b84f42
--- /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 000000000..dc3c5d8eb
--- /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 000000000..712e40585
--- /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 000000000..8eaccb859
--- /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 000000000..429dada10
--- /dev/null
+++ b/res/drawable-finger/star_on.png
Binary files differ
diff --git a/res/drawable-finger/sym_action_add.png b/res/drawable-finger/sym_action_add.png
new file mode 100644
index 000000000..af637b3be
--- /dev/null
+++ b/res/drawable-finger/sym_action_add.png
Binary files differ
diff --git a/res/drawable-finger/sym_action_edit.png b/res/drawable-finger/sym_action_edit.png
new file mode 100644
index 000000000..bacf8500b
--- /dev/null
+++ b/res/drawable-finger/sym_action_edit.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 000000000..12719cce6
--- /dev/null
+++ b/res/drawable-finger/sym_action_map.png
Binary files differ
diff --git a/res/drawable-finger/sym_action_organization.png b/res/drawable-finger/sym_action_organization.png
new file mode 100644
index 000000000..994d3f583
--- /dev/null
+++ b/res/drawable-finger/sym_action_organization.png
Binary files differ
diff --git a/res/drawable-finger/sym_action_remove.png b/res/drawable-finger/sym_action_remove.png
new file mode 100644
index 000000000..f5ca2f90d
--- /dev/null
+++ b/res/drawable-finger/sym_action_remove.png
Binary files differ
diff --git a/res/drawable-finger/sym_action_search.png b/res/drawable-finger/sym_action_search.png
new file mode 100644
index 000000000..dab3ed62e
--- /dev/null
+++ b/res/drawable-finger/sym_action_search.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 000000000..e0ce4bb62
--- /dev/null
+++ b/res/drawable-finger/sym_action_sms.png
Binary files differ
diff --git a/res/drawable-finger/sym_action_view_contact.png b/res/drawable-finger/sym_action_view_contact.png
new file mode 100644
index 000000000..118d8b30a
--- /dev/null
+++ b/res/drawable-finger/sym_action_view_contact.png
Binary files differ
diff --git a/res/drawable-finger/sym_note.png b/res/drawable-finger/sym_note.png
new file mode 100644
index 000000000..e4dbdf576
--- /dev/null
+++ b/res/drawable-finger/sym_note.png
Binary files differ
diff --git a/res/drawable-finger/sym_ringtone.png b/res/drawable-finger/sym_ringtone.png
new file mode 100644
index 000000000..cb2a65bd4
--- /dev/null
+++ b/res/drawable-finger/sym_ringtone.png
Binary files differ
diff --git a/res/drawable-finger/sym_send_to_voicemail.png b/res/drawable-finger/sym_send_to_voicemail.png
new file mode 100644
index 000000000..874311554
--- /dev/null
+++ b/res/drawable-finger/sym_send_to_voicemail.png
Binary files differ
diff --git a/res/drawable-finger/title_bar_shadow.9.png b/res/drawable-finger/title_bar_shadow.9.png
new file mode 100644
index 000000000..087236657
--- /dev/null
+++ b/res/drawable-finger/title_bar_shadow.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 000000000..826656ffb
--- /dev/null
+++ b/res/drawable/ic_launcher_contacts.png
Binary files differ
diff --git a/res/drawable/ic_launcher_contacts_phones.png b/res/drawable/ic_launcher_contacts_phones.png
new file mode 100644
index 000000000..822cc3194
--- /dev/null
+++ b/res/drawable/ic_launcher_contacts_phones.png
Binary files differ
diff --git a/res/drawable/ic_launcher_contacts_starred.png b/res/drawable/ic_launcher_contacts_starred.png
new file mode 100644
index 000000000..328761e8b
--- /dev/null
+++ b/res/drawable/ic_launcher_contacts_starred.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 000000000..4e613ecce
--- /dev/null
+++ b/res/drawable/ic_launcher_phone.png
Binary files differ
diff --git a/res/layout-finger/call_detail.xml b/res/layout-finger/call_detail.xml
new file mode 100644
index 000000000..69bfa9e56
--- /dev/null
+++ b/res/layout-finger/call_detail.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:drawable/title_bar_tall"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:paddingLeft="9dip"
+ android:paddingRight="5dip"
+ >
+
+ <ImageView android:id="@+id/icon"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_marginRight="5dip"
+ />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:layout_marginLeft="5dip"
+ >
+
+ <TextView android:id="@+id/type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+ <TextView android:id="@+id/time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ />
+
+ <TextView android:id="@+id/duration"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_height="0dip"
+ android:layout_width="fill_parent"
+ android:layout_weight="1"
+ >
+ <ListView android:id="@android:id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:scrollbarStyle="outsideOverlay"
+ />
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/title_bar_shadow"
+ />
+
+ </FrameLayout>
+</LinearLayout>
diff --git a/res/layout-finger/call_detail_list_item.xml b/res/layout-finger/call_detail_list_item.xml
new file mode 100644
index 000000000..6c616908f
--- /dev/null
+++ b/res/layout-finger/call_detail_list_item.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:paddingLeft="9dip"
+ android:paddingRight="5dip"
+ android:gravity="center_vertical"
+>
+
+ <ImageView android:id="@+id/icon"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_marginRight="5dip"
+ android:gravity="center"
+ android:scaleType="centerInside"
+ />
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="5dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="7dip"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ >
+
+ <TextView android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+
+ <LinearLayout android:id="@+id/line2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="5dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textStyle="bold"
+ />
+
+ <TextView android:id="@+id/number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout-finger/contacts_list_content.xml b/res/layout-finger/contacts_list_content.xml
new file mode 100644
index 000000000..32b7ffc0f
--- /dev/null
+++ b/res/layout-finger/contacts_list_content.xml
@@ -0,0 +1,45 @@
+<?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">
+
+ <ListView android:id="@android:id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:fastScrollEnabled="true"
+ />
+
+ <ScrollView android:id="@android:id/empty"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:fillViewport="true"
+ >
+ <TextView android:id="@+id/emptyText"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/noContacts"
+ android:textSize="20sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ android:paddingTop="10dip"
+ android:lineSpacingMultiplier="0.92"
+ />
+ </ScrollView>
+</LinearLayout>
diff --git a/res/layout-finger/contacts_list_item.xml b/res/layout-finger/contacts_list_item.xml
new file mode 100644
index 000000000..e695f90cf
--- /dev/null
+++ b/res/layout-finger/contacts_list_item.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="14dip"
+ android:paddingRight="5dip"
+>
+
+ <ImageView android:id="@+id/presence"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_alignParentRight="true"
+ android:layout_marginLeft="5dip"
+ android:layout_centerVertical="true"
+
+ android:gravity="center"
+ android:scaleType="centerInside"
+ />
+
+ <TextView android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="8dip"
+ android:layout_marginTop="-8dip"
+
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textStyle="bold"
+ />
+
+ <TextView android:id="@+id/number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="5dip"
+ android:layout_toRightOf="@id/label"
+ android:layout_alignBaseline="@id/label"
+ android:layout_toLeftOf="@id/presence"
+ android:layout_alignWithParentIfMissing="true"
+
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+ <TextView android:id="@+id/name"
+ android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:layout_above="@id/label"
+ android:layout_alignWithParentIfMissing="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_toLeftOf="@id/presence"
+ android:layout_marginBottom="1dip"
+
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:gravity="center_vertical|left"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+</RelativeLayout>
diff --git a/res/layout-finger/dialer_activity.xml b/res/layout-finger/dialer_activity.xml
new file mode 100644
index 000000000..242821b73
--- /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 000000000..30018f0d3
--- /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/dialpad_chooser_list_item.xml b/res/layout-finger/dialpad_chooser_list_item.xml
new file mode 100644
index 000000000..c5abeaccf
--- /dev/null
+++ b/res/layout-finger/dialpad_chooser_list_item.xml
@@ -0,0 +1,35 @@
+<?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 of a single item in the Dialer's "Dialpad chooser" UI. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <ImageView android:id="@+id/icon"
+ android:layout_width="64px"
+ android:layout_height="64px"
+ android:scaleType="center" />
+
+ <TextView android:id="@+id/text"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_gravity="center_vertical"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/res/layout-finger/edit_contact.xml b/res/layout-finger/edit_contact.xml
new file mode 100644
index 000000000..a3a1849e6
--- /dev/null
+++ b/res/layout-finger/edit_contact.xml
@@ -0,0 +1,117 @@
+<?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: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="wrap_content"
+ android:orientation="horizontal"
+ android:padding="0dip"
+ android:gravity="center_vertical"
+ android:baselineAligned="false"
+ >
+
+ <FrameLayout
+ android:layout_width="76dip"
+ android:layout_height="76dip"
+ android:layout_marginTop="4dip"
+ android:layout_marginLeft="6dip"
+ android:layout_marginBottom="6dip"
+ android:layout_marginRight="2dip"
+ >
+ <ImageView android:id="@+id/photoImage"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:clickable="true"
+ android:focusable="true"
+ android:src="@drawable/ic_menu_add_picture"
+ android:scaleType="center"
+ android:background="@drawable/btn_contact_picture"
+ />
+ </FrameLayout>
+
+ <EditText android:id="@+id/name"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:gravity="center_vertical"
+ android:inputType="textPersonName|textCapWords"
+ 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>
+
+ <!-- "Phonetic name" entry widget, visible only in certain locales -->
+ <include layout="@layout/edit_phonetic_name"/>
+
+ <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"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ style="@android:style/ButtonBar"
+ >
+
+ <Button android:id="@+id/saveButton"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/menu_done"
+ />
+
+ <Button android:id="@+id/discardButton"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/menu_doNotSave"
+ />
+
+ </LinearLayout>
+
+ </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 000000000..d17fdf70b
--- /dev/null
+++ b/res/layout-finger/edit_contact_entry.xml
@@ -0,0 +1,51 @@
+<?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_marginLeft="4dip"
+ android:layout_marginBottom="3dip"
+ android:orientation="horizontal"
+ android:baselineAligned="false"
+ android:gravity="top"
+ android:paddingRight="?android:attr/scrollbarSize"
+ >
+
+ <Button android:id="@+id/label"
+ android:layout_width="80dip"
+ android:layout_height="wrap_content"
+ android:gravity="left|center_vertical"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ />
+
+ <EditText android:id="@+id/data"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="2dip"
+ android:layout_marginRight="2dip"
+ />
+
+ <ImageButton android:id="@+id/delete"
+ style="@style/MinusButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+</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 000000000..9daf3d1b5
--- /dev/null
+++ b/res/layout-finger/edit_contact_entry_org.xml
@@ -0,0 +1,61 @@
+<?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="horizontal"
+ android:baselineAligned="false"
+ android:gravity="top"
+ android:paddingRight="?android:attr/scrollbarSize"
+ >
+
+ <Button android:id="@+id/label"
+ android:layout_width="100dip"
+ android:layout_height="wrap_content"
+ android:gravity="left|center_vertical"
+ />
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:baselineAligned="false"
+ android:gravity="center_vertical"
+ >
+
+ <EditText android:id="@+id/data"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dip"
+ />
+
+ <EditText android:id="@+id/data2"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dip"
+ />
+
+ </LinearLayout>
+
+ <ImageButton android:id="@+id/delete"
+ style="@style/MinusButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+</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 000000000..f2a171f89
--- /dev/null
+++ b/res/layout-finger/edit_contact_entry_ringtone.xml
@@ -0,0 +1,68 @@
+<?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:id="@+id/entry_ringtone"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingRight="?android:attr/scrollbarSize"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:background="@android:drawable/list_selector_background"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:focusable="true"
+ android:clickable="true"
+ >
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="14dip"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="6dip"
+ android:layout_weight="1"
+ android:duplicateParentState="true"
+ >
+
+ <TextView android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:duplicateParentState="true"
+ />
+
+ <TextView android:id="@+id/data"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/label"
+ android:layout_alignLeft="@+id/label"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:maxLines="2"
+ android:duplicateParentState="true"
+ />
+
+ </RelativeLayout>
+
+ <ImageView
+ style="@style/MoreButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+</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 000000000..5e40631e0
--- /dev/null
+++ b/res/layout-finger/edit_contact_entry_static_label.xml
@@ -0,0 +1,54 @@
+<?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_marginLeft="4dip"
+ android:layout_marginBottom="3dip"
+ android:orientation="horizontal"
+ android:baselineAligned="false"
+ android:gravity="top"
+ android:paddingRight="?android:attr/scrollbarSize"
+ >
+
+ <!-- Hide this TextView because this layout is only used for Note entries -->
+ <TextView android:id="@+id/label"
+ android:layout_width="80dip"
+ android:layout_height="wrap_content"
+ android:gravity="left|center_vertical"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:visibility="gone"
+ />
+
+ <EditText android:id="@+id/data"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="2dip"
+ android:layout_marginRight="2dip"
+ android:gravity="top"
+ />
+
+ <ImageButton android:id="@+id/delete"
+ style="@style/MinusButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+</LinearLayout>
diff --git a/res/layout-finger/edit_contact_entry_voicemail.xml b/res/layout-finger/edit_contact_entry_voicemail.xml
new file mode 100644
index 000000000..7b66500d4
--- /dev/null
+++ b/res/layout-finger/edit_contact_entry_voicemail.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/checkable"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingRight="?android:attr/scrollbarSize"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:background="@android:drawable/list_selector_background"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:focusable="true"
+ android:clickable="true"
+ >
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="14dip"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="6dip"
+ android:layout_weight="1"
+ android:duplicateParentState="true"
+ >
+
+ <TextView android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:duplicateParentState="true"
+ />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/label"
+ android:layout_alignLeft="@+id/label"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="@string/send_to_voicemail_checkbox"
+ android:maxLines="2"
+ android:duplicateParentState="true"
+ />
+
+ </RelativeLayout>
+
+ <CheckBox android:id="@+id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:focusable="false"
+ android:clickable="false"
+ />
+
+ <!-- Need to provide a data view to make binder happy -->
+ <TextView android:id="@+id/data"
+ android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:visibility="gone"
+ />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout-finger/edit_divider.xml b/res/layout-finger/edit_divider.xml
new file mode 100644
index 000000000..1dbb56317
--- /dev/null
+++ b/res/layout-finger/edit_divider.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.
+-->
+
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/listDivider"
+ />
diff --git a/res/layout-finger/edit_phonetic_name.xml b/res/layout-finger/edit_phonetic_name.xml
new file mode 100644
index 000000000..afbbb1d5b
--- /dev/null
+++ b/res/layout-finger/edit_phonetic_name.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- "Phonetic name" field on the Edit contact screen.
+
+ In locales where a "phonetic name" is meaningful, like Japanese
+ (which uses a "furigana" or "yomi" field), this layout file
+ should include a visible label and EditText widget.
+
+ In the default locale, though, nothing here is visible.
+ This layout file MUST still provide an EditText widget with
+ id "phonetic_name" in *any* locale, though, since the Java code depends
+ on it. (Even if the EditText is hidden we might still need to store
+ a value there, so we don't *lose* a phonetic name from the database
+ just because the user happened to edit it in a non-Japanese locale.)
+
+ For now, at least, this layout contains a complete (but hidden)
+ "phonetic name" row, so it's easy to test the phonetic_name feature
+ by temporarily removing the visibility="gone" attribute below.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:padding="0dip"
+ android:gravity="center_vertical"
+ android:baselineAligned="false"
+ android:visibility="gone"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:paddingLeft="4dip"
+ android:gravity="left|center_vertical"
+ android:text="@string/label_phonetic_name"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+ <EditText android:id="@+id/phonetic_name"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="4dip"
+ android:gravity="center_vertical"
+ android:inputType="textPersonName|textCapWords"
+ android:hint="@string/ghostData_phonetic_name"
+ android:nextFocusDown="@id/data"
+ />
+</LinearLayout>
diff --git a/res/layout-finger/edit_separator.xml b/res/layout-finger/edit_separator.xml
new file mode 100644
index 000000000..f638ade5d
--- /dev/null
+++ b/res/layout-finger/edit_separator.xml
@@ -0,0 +1,57 @@
+<?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:layout_marginBottom="3dip"
+ android:orientation="vertical"
+ >
+
+ <LinearLayout
+ android:id="@+id/separator"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="14dip"
+ android:layout_marginTop="3dip"
+ android:layout_marginBottom="1dip"
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:orientation="horizontal"
+ android:gravity="bottom"
+ android:focusable="true"
+ android:clickable="true"
+ >
+
+ <TextView android:id="@+id/text"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginBottom="8dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+
+ <ImageView android:id="@+id/add"
+ style="@style/PlusButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:duplicateParentState="true"
+ />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout-finger/edit_separator_alone.xml b/res/layout-finger/edit_separator_alone.xml
new file mode 100644
index 000000000..c42af1c0b
--- /dev/null
+++ b/res/layout-finger/edit_separator_alone.xml
@@ -0,0 +1,48 @@
+<?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:id="@+id/separator"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="14dip"
+ android:paddingRight="?android:attr/scrollbarSize"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:background="@android:drawable/list_selector_background"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:focusable="true"
+ android:clickable="true"
+ >
+
+ <TextView android:id="@+id/text"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:duplicateParentState="true"
+ />
+
+ <ImageView android:id="@+id/add"
+ style="@style/PlusButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ />
+
+</LinearLayout>
diff --git a/res/layout-finger/list_item_text_icons.xml b/res/layout-finger/list_item_text_icons.xml
new file mode 100644
index 000000000..7f6ad1ca7
--- /dev/null
+++ b/res/layout-finger/list_item_text_icons.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:paddingLeft="9dip"
+ android:paddingRight="5dip"
+ android:gravity="center_vertical"
+>
+
+ <ImageView android:id="@+id/icon1"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_marginRight="5dip"
+ android:gravity="center"
+ android:scaleType="centerInside"
+ />
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="5dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="7dip"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ >
+
+ <TextView android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+ <TextView android:id="@android:id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+ </LinearLayout>
+
+ <ImageView android:id="@+id/icon2"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_marginLeft="5dip"
+ android:gravity="center"
+ android:scaleType="centerInside"
+ />
+
+</LinearLayout>
diff --git a/res/layout-finger/list_separator.xml b/res/layout-finger/list_separator.xml
new file mode 100644
index 000000000..5d93d36ce
--- /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="@android:drawable/dark_header"
+ 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 000000000..986d91319
--- /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 000000000..3df1f13f9
--- /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/sync_settings.xml b/res/layout-finger/sync_settings.xml
new file mode 100644
index 000000000..50d0d4737
--- /dev/null
+++ b/res/layout-finger/sync_settings.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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"
+>
+
+ <ListView android:id="@android:id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:drawable/bottom_bar"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ >
+ <Button android:id="@+id/ok"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@android:string/ok"
+ />
+
+ <Button android:id="@+id/cancel"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@android:string/cancel"
+ />
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/layout-finger/twelve_key_dialer.xml b/res/layout-finger/twelve_key_dialer.xml
new file mode 100644
index 000000000..071ca57fe
--- /dev/null
+++ b/res/layout-finger/twelve_key_dialer.xml
@@ -0,0 +1,71 @@
+<?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:id="@+id/digitsAndBackspace"
+ 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" />
+
+ <!-- "Dialpad chooser" UI, shown only when the user brings up the
+ Dialer while a call is already in progress.
+ When this UI is visible, the other Dialer elements
+ (the textfield/button and the dialpad) are hidden. -->
+ <ListView android:id="@+id/dialpadChooser"
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:layout_weight="1"
+ />
+
+</LinearLayout>
diff --git a/res/layout-finger/view_contact.xml b/res/layout-finger/view_contact.xml
new file mode 100644
index 000000000..4a0f25210
--- /dev/null
+++ b/res/layout-finger/view_contact.xml
@@ -0,0 +1,71 @@
+<?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_tall"
+ android:paddingRight="5dip"
+ android:gravity="center_vertical"
+ >
+
+ <ImageView android:id="@+id/photo"
+ style="?android:attr/imageWellStyle"
+ android:layout_width="78dip"
+ android:layout_height="78dip"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="2dip"
+ android:scaleType="fitCenter"
+ android:background="@drawable/btn_contact_picture"
+ />
+
+ <!-- "Name" field is locale-specific. -->
+ <include layout="@layout/view_contact_name"/>
+
+ <CheckBox android:id="@+id/star"
+ style="?android:attr/starStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ >
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/title_bar_shadow"
+ />
+
+ <ListView android:id="@android:id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:scrollbarStyle="outsideOverlay"
+ />
+ </FrameLayout>
+</LinearLayout>
diff --git a/res/layout-finger/view_contact_entry.xml b/res/layout-finger/view_contact_entry.xml
new file mode 100644
index 000000000..772c7d99c
--- /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 000000000..0cfa3c71a
--- /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 000000000..bd8078650
--- /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-finger/view_contact_name.xml b/res/layout-finger/view_contact_name.xml
new file mode 100644
index 000000000..126c69ba0
--- /dev/null
+++ b/res/layout-finger/view_contact_name.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- In the default locale, the "Name" field is a single TextView -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ 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"
+ android:maxLines="2"
+ android:ellipsize="end"
+ />
diff --git a/res/layout-ja-finger/edit_phonetic_name.xml b/res/layout-ja-finger/edit_phonetic_name.xml
new file mode 100644
index 000000000..41a40597c
--- /dev/null
+++ b/res/layout-ja-finger/edit_phonetic_name.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- "Phonetic name" field on the Edit contact screen, for
+ Japanese-language locales (i.e. the "furigana" or "yomi" field.) -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:padding="0dip"
+ android:gravity="center_vertical"
+ android:baselineAligned="false"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:paddingLeft="4dip"
+ android:gravity="left|center_vertical"
+ android:text="@string/label_phonetic_name"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+ <EditText android:id="@+id/phonetic_name"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="4dip"
+ android:gravity="center_vertical"
+ android:inputType="textPersonName|textCapWords"
+ android:hint="@string/ghostData_phonetic_name"
+ android:nextFocusDown="@id/data"
+ />
+</LinearLayout>
diff --git a/res/layout-ja-finger/view_contact_name.xml b/res/layout-ja-finger/view_contact_name.xml
new file mode 100644
index 000000000..bd72bba9d
--- /dev/null
+++ b/res/layout-ja-finger/view_contact_name.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- In Japanese-language locales, the "Name" field contains two separate
+ TextViews: the name itself, and also the phonetic ("furigana") field. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+
+ <TextView android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:shadowColor="#BB000000"
+ android:shadowRadius="2.75"
+ />
+
+ <TextView android:id="@+id/phonetic_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:shadowColor="#BB000000"
+ android:shadowRadius="2.75"
+ />
+
+</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 000000000..2b436b880
--- /dev/null
+++ b/res/layout-land-finger/twelve_key_dialer.xml
@@ -0,0 +1,72 @@
+<?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:id="@+id/digitsAndBackspace"
+ 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>
+
+ <!-- "Dialpad chooser" UI, shown only when the user brings up the
+ Dialer while a call is already in progress.
+ When this UI is visible, the other Dialer elements
+ (the textfield and button) are hidden. -->
+ <ListView android:id="@+id/dialpadChooser"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:footerDividersEnabled="true"
+ />
+
+</LinearLayout>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
new file mode 100644
index 000000000..e82b09bae
--- /dev/null
+++ b/res/values-cs/strings.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"Kontakty"</string>
+ <!-- no translation found for launcherDialer (140610573639849799) -->
+ <skip />
+ <string name="shortcutContact">"Kontakt"</string>
+ <string name="starredList">"Označené hvězdičkou"</string>
+ <string name="frequentList">"Časté"</string>
+ <string name="strequentList">"Oblíbené"</string>
+ <string name="viewContactTitle">"Kontaktní informace"</string>
+ <string name="viewContactDesription">"Zobrazit kontakt"</string>
+ <string name="editContactDescription">"Upravit kontakt"</string>
+ <string name="insertContactDescription">"Vytvořit kontakt"</string>
+ <string name="searchHint">"Vyhledat kontakty"</string>
+ <string name="menu_newContact">"Nový kontakt"</string>
+ <string name="menu_viewContact">"Zobrazit kontakt"</string>
+ <string name="menu_callNumber">"Volat kontakt <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="menu_addStar">"Přidat do oblíbených"</string>
+ <string name="menu_removeStar">"Odebrat z oblíbených položek"</string>
+ <string name="menu_showBarcode">"Zobrazit čárový kód"</string>
+ <string name="menu_editContact">"Upravit kontakt"</string>
+ <string name="menu_deleteContact">"Smazat kontakt"</string>
+ <string name="menu_call">"Volat"</string>
+ <string name="menu_sendSMS">"Odeslat SMS nebo MMS"</string>
+ <string name="menu_sendEmail">"Odeslat e-mail"</string>
+ <string name="menu_viewAddress">"Adresa na mapě"</string>
+ <string name="menu_makeDefaultNumber">"Nastavit jako výchozí číslo"</string>
+ <string name="deleteConfirmation_title">"Smazat"</string>
+ <string name="deleteConfirmation">"Tento kontakt bude smazán."</string>
+ <string name="menu_done">"Hotovo"</string>
+ <!-- no translation found for menu_doNotSave (2174577548513895144) -->
+ <skip />
+ <string name="editContact_title_edit">"Upravit kontakt"</string>
+ <string name="editContact_title_insert">"Nový kontakt"</string>
+ <string name="menu_addItem">"Další informace"</string>
+ <string name="label_phonetic_name">"Foneticky"</string>
+ <string name="label_notes">"Poznámky"</string>
+ <string name="label_ringtone">"Vyzváněcí tón"</string>
+ <string name="ghostData_name">"Jméno a příjmení"</string>
+ <string name="ghostData_phonetic_name">"Jméno (foneticky)"</string>
+ <string name="ghostData_company">"Společnost"</string>
+ <string name="ghostData_title">"Funkce"</string>
+ <string name="ghostData_im">"Název v chatu"</string>
+ <string name="ghostData_notes">"Má poznámka"</string>
+ <string name="ghostData_phone">"Telefonní číslo"</string>
+ <string name="ghostData_email">"E-mailová adresa"</string>
+ <string name="ghostData_postal">"Poštovní adresa"</string>
+ <string name="invalidContactMessage">"Kontakt neexistuje."</string>
+ <string name="pickerNewContactHeader">"Vytvořit nový kontakt"</string>
+ <string name="selectLabel">"Vyberte štítek"</string>
+ <string name="phoneLabelsGroup">"Telefon"</string>
+ <string name="emailLabelsGroup">"E-mail"</string>
+ <string name="imLabelsGroup">"Chat"</string>
+ <string name="postalLabelsGroup">"Poštovní adresa"</string>
+ <string name="otherLabelsGroup">"Ostatní"</string>
+ <string-array name="otherLabels">
+ <item>"Organizace"</item>
+ <item>"Poznámka"</item>
+ </string-array>
+ <string name="errorDialogTitle">"Žádné fotografie"</string>
+ <string name="photoPickerNotFoundText">"V telefonu nejsou žádné fotografie."</string>
+ <string name="attachToContact">"Ikona kontaktu"</string>
+ <string name="customLabelPickerTitle">"Název vlastního štítku"</string>
+ <string name="menu_displayGroup">"Zobrazit skupinu"</string>
+ <string name="syncGroupPreference">"Upravit synchronizované skupiny"</string>
+ <string name="importFromSim">"Importovat kontakty"</string>
+ <string name="send_to_voicemail_checkbox">"Odesílat hovory přímo do hlasové pošty"</string>
+ <string name="send_to_voicemail_view">"Hovory jsou přesměrovány přímo do hlasové schránky."</string>
+ <string name="default_ringtone">"Výchozí"</string>
+ <string name="addPicture">"Přidat ikonu"</string>
+ <string name="removePicture">"Odstranit ikonu"</string>
+ <string name="noContacts">"Žádné kontakty."</string>
+ <string name="noContactsWithPhoneNumbers">"Ke kontaktům nejsou přiřazena žádná telefonní čísla."</string>
+ <string name="noFavorites">"Žádné oblíbené položky."</string>
+ <string name="select_group_title">"Skupiny"</string>
+ <string name="groupEmpty">"Skupina <xliff:g id="GROUPNAME">%s</xliff:g> je prázdná."</string>
+ <string name="showAllGroups">"Všechny kontakty"</string>
+ <string name="syncAllGroups">"Synchronizovat všechny kontakty"</string>
+ <string name="groupNameMyContacts">"Moje kontakty"</string>
+ <string name="groupNameWithPhones">"Kontakty s telefonním číslem"</string>
+ <string name="contactCreatedToast">"Kontakt byl vytvořen."</string>
+ <string name="contactSavedToast">"Kontakt byl uložen."</string>
+ <string name="listSeparatorCallNumber">"Vytočit číslo"</string>
+ <string name="listSeparatorCallNumber_edit">"Telefonní čísla"</string>
+ <string name="listSeparatorSendSmsMms">"Odeslat SMS nebo MMS"</string>
+ <string name="listSeparatorSendEmail">"Odeslat e-mail"</string>
+ <string name="listSeparatorSendEmail_edit">"E-mailové adresy"</string>
+ <string name="listSeparatorSendIm">"Odeslat zprávu chatu"</string>
+ <string name="listSeparatorSendIm_edit">"Adresa chatu"</string>
+ <string name="listSeparatorMapAddress">"Adresa na mapě"</string>
+ <string name="listSeparatorMapAddress_edit">"Poštovní adresa"</string>
+ <string name="listSeparatorOrganizations">"Organizace"</string>
+ <string name="listSeparatorOtherInformation">"Ostatní informace"</string>
+ <string name="listSeparatorOtherInformation_edit">"Další možnosti"</string>
+ <string name="listSeparatorMore_edit">"Další"</string>
+ <string name="contactsIconLabel">"Kontakty"</string>
+ <string name="contactsFavoritesLabel">"Oblíbené"</string>
+ <string name="dialerIconLabel">"Vytáčení"</string>
+ <string name="recentCallsIconLabel">"Hovory"</string>
+ <string name="liveFolderAll">"Všechny kontakty"</string>
+ <string name="liveFolderFavorites">"Kontakty označené hvězdičkou"</string>
+ <string name="liveFolderPhone">"Kontakty s telefonními čísly"</string>
+ <string name="menu_sendTextMessage">"Odeslat zprávu SMS"</string>
+ <string name="recentCalls_callNumber">"Volat kontakt <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="recentCalls_editNumberBeforeCall">"Před voláním upravit číslo"</string>
+ <string name="recentCalls_addToContact">"Přidat mezi kontakty"</string>
+ <string name="recentCalls_removeFromRecentList">"Odstranit z hovorů"</string>
+ <string name="recentCalls_deleteAll">"Vymazat hovory"</string>
+ <string name="recentCalls_empty">"Záznam hovorů je prázdný."</string>
+ <string name="imei">"IMEI"</string>
+ <string name="voicemail">"Hlasová schránka"</string>
+ <string name="unknown">"Neznámé číslo"</string>
+ <string name="private_num">"Soukromé číslo"</string>
+ <string name="dialerKeyboardHintText">"Číslo vytočíte pomocí klávesnice"</string>
+ <string name="dialerDialpadHintText">"Vytočením přidat hovor"</string>
+ <string name="simContacts_emptyLoading">"Načítání z karty SIM..."</string>
+ <string name="simContacts_title">"Kontakty na kartě SIM"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"Synchronizujte své kontakty Google!"</font>" "\n"Po synchronizaci telefonu budete mít kontaktní informace vždy u sebe."</string>
+ <!-- no translation found for noContactsHelpText (2249195687463896364) -->
+ <skip />
+ <!-- no translation found for noContactsHelpTextWithSync (4927701496550314555) -->
+ <skip />
+ <string name="seclectSyncGroups_title">"Vyberte skupiny, které chcete synchronizovat"</string>
+ <string name="liveFolder_all_label">"Všechny kontakty"</string>
+ <string name="liveFolder_favorites_label">"S hvězdičkou"</string>
+ <string name="liveFolder_phones_label">"Telefony"</string>
+ <string name="dialer_useDtmfDialpad">"Použít dotykovou tónovou klávesnici"</string>
+ <string name="dialer_returnToInCallScreen">"Návrat k probíhajícímu hovoru"</string>
+ <string name="dialer_addAnotherCall">"Přidat hovor"</string>
+ <string name="callDetailTitle">"Detaily hovoru"</string>
+ <string name="toast_call_detail_error">"Detaily požadovaného hovoru nelze načíst."</string>
+ <string name="call_type">"Typ"</string>
+ <string name="type_incoming">"Příchozí volání"</string>
+ <string name="type_outgoing">"Odchozí volání"</string>
+ <string name="type_missed">"Zmeškané volání"</string>
+ <string name="call_time">"Čas"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g> (<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"Trvání"</string>
+ <string name="header_actions">"Akce"</string>
+ <string name="ringtone_spinner">"Vyzváněcí tón: <xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"Přidat další položky"</string>
+ <string name="actionCall">"Volat: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionEmail">"E-mail: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionText">"Text: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionChat">"Chatovat pomocí služby <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionMap">"Zobrazit adresu: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionIncomingCall">"Příchozí volání"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"Budou odeslány přímo do hlasové pošty."</string>
+ <string name="detailsRingtone">"Nastavit vyzvánění <xliff:g id="RINGTONE_NAME">%s</xliff:g>"</string>
+ <string name="callBack">"Zavolat zpět"</string>
+ <!-- no translation found for callAgain (3197312117049874778) -->
+ <skip />
+ <!-- no translation found for returnCall (8171961914203617813) -->
+ <skip />
+ <!-- no translation found for callDetailsDurationFormat (8157706382818184268) -->
+ <skip />
+</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
new file mode 100644
index 000000000..507d69d04
--- /dev/null
+++ b/res/values-de/strings.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"Kontakte"</string>
+ <!-- no translation found for launcherDialer (140610573639849799) -->
+ <skip />
+ <string name="shortcutContact">"Kontakt"</string>
+ <string name="starredList">"Markiert"</string>
+ <string name="frequentList">"Häufig"</string>
+ <string name="strequentList">"Favoriten"</string>
+ <string name="viewContactTitle">"Kontaktinformationen"</string>
+ <string name="viewContactDesription">"Kontakt anzeigen"</string>
+ <string name="editContactDescription">"Kontakt bearbeiten"</string>
+ <string name="insertContactDescription">"Kontakt erstellen"</string>
+ <string name="searchHint">"Kontakte durchsuchen"</string>
+ <string name="menu_newContact">"Neuer Kontakt"</string>
+ <string name="menu_viewContact">"Kontakt anzeigen"</string>
+ <string name="menu_callNumber">"<xliff:g id="NAME">%s</xliff:g> anrufen"</string>
+ <string name="menu_addStar">"Favoriten hinzufügen"</string>
+ <string name="menu_removeStar">"Aus Favoriten entfernen"</string>
+ <string name="menu_showBarcode">"Barcode anzeigen"</string>
+ <string name="menu_editContact">"Kontakt bearbeiten"</string>
+ <string name="menu_deleteContact">"Kontakt löschen"</string>
+ <string name="menu_call">"Anruf"</string>
+ <string name="menu_sendSMS">"SMS/MMS senden"</string>
+ <string name="menu_sendEmail">"E-Mail senden"</string>
+ <string name="menu_viewAddress">"Adresse auf der Karte"</string>
+ <string name="menu_makeDefaultNumber">"Als Standardnummer festlegen"</string>
+ <string name="deleteConfirmation_title">"Löschen"</string>
+ <string name="deleteConfirmation">"Dieser Kontakt wird gelöscht."</string>
+ <string name="menu_done">"Fertig"</string>
+ <!-- no translation found for menu_doNotSave (2174577548513895144) -->
+ <skip />
+ <string name="editContact_title_edit">"Kontakt bearbeiten"</string>
+ <string name="editContact_title_insert">"Neuer Kontakt"</string>
+ <string name="menu_addItem">"Weitere Informationen"</string>
+ <string name="label_phonetic_name">"Phonetisch"</string>
+ <string name="label_notes">"Notizen"</string>
+ <string name="label_ringtone">"Klingelton"</string>
+ <string name="ghostData_name">"Vor- und Nachname"</string>
+ <string name="ghostData_phonetic_name">"Phonetischer Name"</string>
+ <string name="ghostData_company">"Unternehmen"</string>
+ <string name="ghostData_title">"Position"</string>
+ <string name="ghostData_im">"Chat-Name"</string>
+ <string name="ghostData_notes">"Meine Notiz"</string>
+ <string name="ghostData_phone">"Telefonnummer"</string>
+ <string name="ghostData_email">"E-Mail-Adresse"</string>
+ <string name="ghostData_postal">"Postanschrift"</string>
+ <string name="invalidContactMessage">"Dieser Kontakt existiert nicht."</string>
+ <string name="pickerNewContactHeader">"Neuen Kontakt erstellen"</string>
+ <string name="selectLabel">"Wählen Sie ein Label aus."</string>
+ <string name="phoneLabelsGroup">"Telefon"</string>
+ <string name="emailLabelsGroup">"E-Mail"</string>
+ <string name="imLabelsGroup">"Chat"</string>
+ <string name="postalLabelsGroup">"Postanschrift"</string>
+ <string name="otherLabelsGroup">"Sonstiges"</string>
+ <string-array name="otherLabels">
+ <item>"Firma/Organisation"</item>
+ <item>"Notiz"</item>
+ </string-array>
+ <string name="errorDialogTitle">"Keine Bilder"</string>
+ <string name="photoPickerNotFoundText">"Auf dem Telefon sind keine Bilder verfügbar."</string>
+ <string name="attachToContact">"Kontaktsymbol"</string>
+ <string name="customLabelPickerTitle">"Name des benutzerdefinierten Labels"</string>
+ <string name="menu_displayGroup">"Gruppe anzeigen"</string>
+ <string name="syncGroupPreference">"Synchronisierte Gruppen bearbeiten"</string>
+ <string name="importFromSim">"Kontakte importieren"</string>
+ <string name="send_to_voicemail_checkbox">"Anrufe direkt an Voicemail senden"</string>
+ <string name="send_to_voicemail_view">"Anrufe werden direkt an Mailbox gesendet"</string>
+ <string name="default_ringtone">"Standard"</string>
+ <string name="addPicture">"Symbol hinzufügen"</string>
+ <string name="removePicture">"Symbol entfernen"</string>
+ <string name="noContacts">"Keine Kontakte"</string>
+ <string name="noContactsWithPhoneNumbers">"Keine Kontakte mit Telefonnummern"</string>
+ <string name="noFavorites">"Keine Favoriten"</string>
+ <string name="select_group_title">"Gruppen"</string>
+ <string name="groupEmpty">"Ihre Gruppe \"<xliff:g id="GROUPNAME">%s</xliff:g>\" ist leer."</string>
+ <string name="showAllGroups">"Alle Kontakte"</string>
+ <string name="syncAllGroups">"Alle Kontakte synchronisieren"</string>
+ <string name="groupNameMyContacts">"Meine Kontakte"</string>
+ <string name="groupNameWithPhones">"Kontakte mit Telefonnummern"</string>
+ <string name="contactCreatedToast">"Kontakt erstellt"</string>
+ <string name="contactSavedToast">"Kontakt gespeichert"</string>
+ <string name="listSeparatorCallNumber">"Rufnummer"</string>
+ <string name="listSeparatorCallNumber_edit">"Telefonnummern"</string>
+ <string name="listSeparatorSendSmsMms">"SMS/MMS senden"</string>
+ <string name="listSeparatorSendEmail">"E-Mail senden"</string>
+ <string name="listSeparatorSendEmail_edit">"E-Mail-Adressen"</string>
+ <string name="listSeparatorSendIm">"Per Chat kontaktieren"</string>
+ <string name="listSeparatorSendIm_edit">"Chat-Adressen"</string>
+ <string name="listSeparatorMapAddress">"Adresse in Maps"</string>
+ <string name="listSeparatorMapAddress_edit">"Postanschrift"</string>
+ <string name="listSeparatorOrganizations">"Organisationen"</string>
+ <string name="listSeparatorOtherInformation">"Mehr Informationen"</string>
+ <string name="listSeparatorOtherInformation_edit">"Sonstige Optionen"</string>
+ <string name="listSeparatorMore_edit">"Mehr"</string>
+ <string name="contactsIconLabel">"Kontakte"</string>
+ <string name="contactsFavoritesLabel">"Favoriten"</string>
+ <string name="dialerIconLabel">"Telefon"</string>
+ <string name="recentCallsIconLabel">"Anrufe"</string>
+ <string name="liveFolderAll">"Alle Kontakte"</string>
+ <string name="liveFolderFavorites">"Markierte Kontakte"</string>
+ <string name="liveFolderPhone">"Kontakte mit Telefonnummern"</string>
+ <string name="menu_sendTextMessage">"SMS-Nachricht senden"</string>
+ <string name="recentCalls_callNumber">"<xliff:g id="NAME">%s</xliff:g> anrufen"</string>
+ <string name="recentCalls_editNumberBeforeCall">"Nummer vor Anruf bearbeiten"</string>
+ <string name="recentCalls_addToContact">"Zu Kontakten hinzufügen"</string>
+ <string name="recentCalls_removeFromRecentList">"Aus Anrufliste entfernen"</string>
+ <string name="recentCalls_deleteAll">"Anrufliste löschen"</string>
+ <string name="recentCalls_empty">"Anrufliste ist leer"</string>
+ <string name="imei">"IMEI"</string>
+ <string name="voicemail">"Mailbox"</string>
+ <string name="unknown">"Unbekannt"</string>
+ <string name="private_num">"Private Nummer"</string>
+ <string name="dialerKeyboardHintText">"Mit Tastatur wählen"</string>
+ <string name="dialerDialpadHintText">"Zum Hinzufügen eines Anrufs wählen"</string>
+ <string name="simContacts_emptyLoading">"Ladevorgang von SIM-Karte läuft..."</string>
+ <string name="simContacts_title">"Kontakte auf SIM-Karte"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"Synchronisieren Sie Ihre Google-Kontakte!"</font>" "\n"Nach der Synchronisierung stehen Ihnen Ihre Kontakte überall zur Verfügung."</string>
+ <!-- no translation found for noContactsHelpText (2249195687463896364) -->
+ <skip />
+ <!-- no translation found for noContactsHelpTextWithSync (4927701496550314555) -->
+ <skip />
+ <string name="seclectSyncGroups_title">"Zu synchronisierende Gruppen auswählen"</string>
+ <string name="liveFolder_all_label">"Alle Kontakte"</string>
+ <string name="liveFolder_favorites_label">"Markiert"</string>
+ <string name="liveFolder_phones_label">"Telefone"</string>
+ <string name="dialer_useDtmfDialpad">"Telefontastatur verwenden"</string>
+ <string name="dialer_returnToInCallScreen">"Zurück zum Anruf läuft"</string>
+ <string name="dialer_addAnotherCall">"Anruf hinzufügen"</string>
+ <string name="callDetailTitle">"Anrufdetails"</string>
+ <string name="toast_call_detail_error">"Details für den angeforderten Anruf konnten nicht gelesen werden."</string>
+ <string name="call_type">"Typ"</string>
+ <string name="type_incoming">"Eingehender Anruf"</string>
+ <string name="type_outgoing">"Ausgehender Anruf"</string>
+ <string name="type_missed">"Verpasster Anruf"</string>
+ <string name="call_time">"Zeit"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g> (<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"Dauer"</string>
+ <string name="header_actions">"Aktionen"</string>
+ <string name="ringtone_spinner">"Klingelton: <xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"Weitere Elemente hinzufügen"</string>
+ <string name="actionCall">"<xliff:g id="TYPE">%s</xliff:g> anrufen"</string>
+ <string name="actionEmail">"E-Mail an <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionText">"Text <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionChat">"Chat mit <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionMap">"<xliff:g id="TYPE">%s</xliff:g>-Adresse anzeigen"</string>
+ <string name="actionIncomingCall">"Eingehende Anrufe"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"Wird direkt an Voicemail weitergeleitet"</string>
+ <string name="detailsRingtone">"Eingestellt auf <xliff:g id="RINGTONE_NAME">%s</xliff:g>"</string>
+ <string name="callBack">"Rückruf"</string>
+ <!-- no translation found for callAgain (3197312117049874778) -->
+ <skip />
+ <!-- no translation found for returnCall (8171961914203617813) -->
+ <skip />
+ <!-- no translation found for callDetailsDurationFormat (8157706382818184268) -->
+ <skip />
+</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
new file mode 100644
index 000000000..f41406d8e
--- /dev/null
+++ b/res/values-es/strings.xml
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"Contactos"</string>
+ <string name="launcherDialer">"Llamadas"</string>
+ <string name="shortcutContact">"Contacto"</string>
+ <string name="starredList">"Destacados"</string>
+ <string name="frequentList">"Frecuentes"</string>
+ <string name="strequentList">"Favoritos"</string>
+ <string name="viewContactTitle">"Detalles del contacto"</string>
+ <string name="viewContactDesription">"Ver contacto"</string>
+ <string name="editContactDescription">"Editar contacto"</string>
+ <string name="insertContactDescription">"Crear contacto"</string>
+ <string name="searchHint">"Buscar contactos"</string>
+ <string name="menu_newContact">"Contacto nuevo"</string>
+ <string name="menu_viewContact">"Ver contacto"</string>
+ <string name="menu_callNumber">"Llamar a <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="menu_addStar">"Añadir a \"Favoritos\""</string>
+ <string name="menu_removeStar">"Eliminar de \"Favoritos\""</string>
+ <string name="menu_showBarcode">"Mostrar código de barras"</string>
+ <string name="menu_editContact">"Editar contacto"</string>
+ <string name="menu_deleteContact">"Eliminar contacto"</string>
+ <string name="menu_call">"Llamar"</string>
+ <string name="menu_sendSMS">"Enviar SMS/MMS"</string>
+ <string name="menu_sendEmail">"Enviar mensaje de correo electrónico"</string>
+ <string name="menu_viewAddress">"Dirección en mapa"</string>
+ <string name="menu_makeDefaultNumber">"Convertir en número predeterminado"</string>
+ <string name="deleteConfirmation_title">"Suprimir"</string>
+ <string name="deleteConfirmation">"El contacto se eliminará."</string>
+ <string name="menu_done">"Listo"</string>
+ <string name="menu_doNotSave">"Volver"</string>
+ <string name="editContact_title_edit">"Editar contacto"</string>
+ <string name="editContact_title_insert">"Contacto nuevo"</string>
+ <string name="menu_addItem">"Más información"</string>
+ <string name="label_phonetic_name">"Pronunciación"</string>
+ <string name="label_notes">"Notas"</string>
+ <string name="label_ringtone">"Tono"</string>
+ <string name="ghostData_name">"Nombre y apellido"</string>
+ <string name="ghostData_phonetic_name">"Transcripción fonética del nombre"</string>
+ <string name="ghostData_company">"Empresa"</string>
+ <string name="ghostData_title">"Posición"</string>
+ <string name="ghostData_im">"Nombre de MI"</string>
+ <string name="ghostData_notes">"Mi nota"</string>
+ <string name="ghostData_phone">"Número de teléfono"</string>
+ <string name="ghostData_email">"Dirección de correo electrónico"</string>
+ <string name="ghostData_postal">"Dirección postal"</string>
+ <string name="invalidContactMessage">"Ese contacto no existe."</string>
+ <string name="pickerNewContactHeader">"Crear contacto nuevo"</string>
+ <string name="selectLabel">"Seleccionar etiqueta"</string>
+ <string name="phoneLabelsGroup">"Teléfono"</string>
+ <string name="emailLabelsGroup">"Correo electrónico"</string>
+ <string name="imLabelsGroup">"MI"</string>
+ <string name="postalLabelsGroup">"Dirección postal"</string>
+ <string name="otherLabelsGroup">"Otros"</string>
+ <string-array name="otherLabels">
+ <item>"Organización"</item>
+ <item>"Nota"</item>
+ </string-array>
+ <string name="errorDialogTitle">"Ninguna imagen"</string>
+ <string name="photoPickerNotFoundText">"No hay ninguna imagen disponible en el teléfono."</string>
+ <string name="attachToContact">"Icono de contacto"</string>
+ <string name="customLabelPickerTitle">"Nombre de etiqueta personalizada"</string>
+ <string name="menu_displayGroup">"Mostrar grupo"</string>
+ <string name="syncGroupPreference">"Editar grupos de sincronización"</string>
+ <string name="importFromSim">"Importar contactos"</string>
+ <string name="send_to_voicemail_checkbox">"Enviar llamadas directamente como mensajes de voz"</string>
+ <string name="send_to_voicemail_view">"Las llamadas se envían directamente al buzón de voz."</string>
+ <string name="default_ringtone">"Predeterminado"</string>
+ <string name="addPicture">"Añadir icono"</string>
+ <string name="removePicture">"Eliminar icono"</string>
+ <string name="noContacts">"No hay ningún contacto."</string>
+ <string name="noContactsWithPhoneNumbers">"No hay ningún contacto con número de teléfono."</string>
+ <string name="noFavorites">"No hay ningún favorito."</string>
+ <string name="select_group_title">"Grupos"</string>
+ <string name="groupEmpty">"El grupo \"<xliff:g id="GROUPNAME">%s</xliff:g>\" está vacío."</string>
+ <string name="showAllGroups">"Todos los contactos"</string>
+ <string name="syncAllGroups">"Sincronizar todos los contactos"</string>
+ <string name="groupNameMyContacts">"Mis contactos"</string>
+ <string name="groupNameWithPhones">"Contactos con números de teléfono"</string>
+ <string name="contactCreatedToast">"Se ha creado el contacto."</string>
+ <string name="contactSavedToast">"El contacto se ha guardado."</string>
+ <string name="listSeparatorCallNumber">"Marcar número"</string>
+ <string name="listSeparatorCallNumber_edit">"Números de teléfono"</string>
+ <string name="listSeparatorSendSmsMms">"Enviar SMS/MMS"</string>
+ <string name="listSeparatorSendEmail">"Enviar mensaje de correo electrónico"</string>
+ <string name="listSeparatorSendEmail_edit">"Direcciones de correo electrónico"</string>
+ <string name="listSeparatorSendIm">"Enviar mensaje instantáneo"</string>
+ <string name="listSeparatorSendIm_edit">"Direcciones de chat"</string>
+ <string name="listSeparatorMapAddress">"Dirección en mapa"</string>
+ <string name="listSeparatorMapAddress_edit">"Direcciones postales"</string>
+ <string name="listSeparatorOrganizations">"Organizaciones"</string>
+ <string name="listSeparatorOtherInformation">"Otra información"</string>
+ <string name="listSeparatorOtherInformation_edit">"Otras opciones"</string>
+ <string name="listSeparatorMore_edit">"Más"</string>
+ <string name="contactsIconLabel">"Contactos"</string>
+ <string name="contactsFavoritesLabel">"Favoritos"</string>
+ <string name="dialerIconLabel">"Llamar"</string>
+ <string name="recentCallsIconLabel">"Registro de llamadas"</string>
+ <string name="liveFolderAll">"Todos los contactos"</string>
+ <string name="liveFolderFavorites">"Contactos destacados"</string>
+ <string name="liveFolderPhone">"Contactos con números de teléfono"</string>
+ <string name="menu_sendTextMessage">"Enviar mensaje SMS"</string>
+ <string name="recentCalls_callNumber">"Llamar a <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="recentCalls_editNumberBeforeCall">"Editar número antes de llamar"</string>
+ <string name="recentCalls_addToContact">"Añadir a contactos"</string>
+ <string name="recentCalls_removeFromRecentList">"Eliminar del registro de llamadas"</string>
+ <string name="recentCalls_deleteAll">"Borrar registro de llamadas"</string>
+ <string name="recentCalls_empty">"El registro de llamadas está vacío."</string>
+ <string name="imei">"IMEI"</string>
+ <string name="voicemail">"Buzón de voz"</string>
+ <string name="unknown">"Desconocidos"</string>
+ <string name="private_num">"Número privado"</string>
+ <string name="dialerKeyboardHintText">"Utilizar teclado para marcar"</string>
+ <string name="dialerDialpadHintText">"Marcar para añadir una llamada"</string>
+ <string name="simContacts_emptyLoading">"Cargando desde tarjeta SIM…"</string>
+ <string name="simContacts_title">"Contactos de tarjeta SIM"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"Sincroniza tus contactos de Google"</font>" "\n"Después de sincronizarlos con tu teléfono, podrás acceder a tus contactos desde cualquier lugar."</string>
+ <string name="noContactsHelpText">"No tienes contactos."\n\n"Para añadir contactos, pulsa la tecla "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" y selecciona:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para añadir un nuevo contacto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar contactos"</b></font>" para añadir contactos de la tarjeta SIM"\n</li></string>
+ <string name="noContactsHelpTextWithSync">"No tienes contactos."\n\n"Para añadir contactos, pulsa la tecla "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" y selecciona:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Editar grupos de sincronización"</b></font>" para añadirlos desde una cuenta de Google nueva o existente"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un contacto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar contactos"</b></font>" para añadir contactos de la tarjeta SIM"\n</li></string>
+ <string name="seclectSyncGroups_title">"Seleccionar grupos para la sincronización"</string>
+ <string name="liveFolder_all_label">"Todos los contactos"</string>
+ <string name="liveFolder_favorites_label">"Destacados"</string>
+ <string name="liveFolder_phones_label">"Teléfonos"</string>
+ <string name="dialer_useDtmfDialpad">"Usar el teclado de tonos"</string>
+ <string name="dialer_returnToInCallScreen">"Volver a la llamada en curso"</string>
+ <string name="dialer_addAnotherCall">"Añadir llamada"</string>
+ <string name="callDetailTitle">"Detalles de la llamada"</string>
+ <string name="toast_call_detail_error">"No se han podido leer los detalles de la llamada solicitada."</string>
+ <string name="call_type">"Tipo"</string>
+ <string name="type_incoming">"Llamada entrante"</string>
+ <string name="type_outgoing">"Llamada saliente"</string>
+ <string name="type_missed">"Llamada perdida"</string>
+ <string name="call_time">"Hora"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g> (<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"Duración"</string>
+ <string name="header_actions">"Acciones"</string>
+ <string name="ringtone_spinner">"Tono de llamada: <xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"Añadir más elementos"</string>
+ <string name="actionCall">"Llamar a <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionEmail">"Enviar email a <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionText">"Enviar SMS a <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionChat">"Chatear mediante <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionMap">"Ver dirección de <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionIncomingCall">"Llamadas entrantes"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"Pasarán directamente al correo de voz"</string>
+ <string name="detailsRingtone">"Establecido como <xliff:g id="RINGTONE_NAME">%s</xliff:g>"</string>
+ <string name="callBack">"Volver a llamar"</string>
+ <string name="callAgain">"Llamar de nuevo"</string>
+ <string name="returnCall">"Devolver llamada"</string>
+ <string name="callDetailsDurationFormat">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> seg."</string>
+</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
new file mode 100644
index 000000000..ce008131c
--- /dev/null
+++ b/res/values-fr/strings.xml
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"Contacts"</string>
+ <string name="launcherDialer">"Appeler"</string>
+ <string name="shortcutContact">"Contact"</string>
+ <string name="starredList">"Marqués d\'une étoile"</string>
+ <string name="frequentList">"Contacts fréquents"</string>
+ <string name="strequentList">"Favoris"</string>
+ <string name="viewContactTitle">"Détails du contact"</string>
+ <string name="viewContactDesription">"Afficher le contact"</string>
+ <string name="editContactDescription">"Modifier le contact"</string>
+ <string name="insertContactDescription">"Créer un contact"</string>
+ <string name="searchHint">"Rechercher des contacts"</string>
+ <string name="menu_newContact">"Nouveau contact"</string>
+ <string name="menu_viewContact">"Afficher le contact"</string>
+ <string name="menu_callNumber">"Appeler <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="menu_addStar">"Ajouter aux favoris"</string>
+ <string name="menu_removeStar">"Supprimer des favoris"</string>
+ <string name="menu_showBarcode">"Afficher le code-barres"</string>
+ <string name="menu_editContact">"Modifier le contact"</string>
+ <string name="menu_deleteContact">"Supprimer le contact"</string>
+ <string name="menu_call">"Appel"</string>
+ <string name="menu_sendSMS">"Envoyer un SMS/MMS"</string>
+ <string name="menu_sendEmail">"Envoyer un e-mail"</string>
+ <string name="menu_viewAddress">"Adresse sur un plan"</string>
+ <string name="menu_makeDefaultNumber">"Numéro téléphone par défaut"</string>
+ <string name="deleteConfirmation_title">"Supprimer"</string>
+ <string name="deleteConfirmation">"Ce contact sera supprimé."</string>
+ <string name="menu_done">"OK"</string>
+ <string name="menu_doNotSave">"Annuler"</string>
+ <string name="editContact_title_edit">"Modifier le contact"</string>
+ <string name="editContact_title_insert">"Nouveau contact"</string>
+ <string name="menu_addItem">"Plus d\'infos"</string>
+ <string name="label_phonetic_name">"Prononciation phonétique"</string>
+ <string name="label_notes">"Notes"</string>
+ <string name="label_ringtone">"Sonnerie"</string>
+ <string name="ghostData_name">"Nom et prénom"</string>
+ <string name="ghostData_phonetic_name">"Nom phonétique"</string>
+ <string name="ghostData_company">"Société"</string>
+ <string name="ghostData_title">"Position"</string>
+ <string name="ghostData_im">"Nom du compte"</string>
+ <string name="ghostData_notes">"Mon commentaire"</string>
+ <string name="ghostData_phone">"Numéro téléphone"</string>
+ <string name="ghostData_email">"Adresse e-mail"</string>
+ <string name="ghostData_postal">"Adresse postale"</string>
+ <string name="invalidContactMessage">"Ce contact n\'existe pas."</string>
+ <string name="pickerNewContactHeader">"Créer un nouveau contact"</string>
+ <string name="selectLabel">"Sélectionnez un libellé"</string>
+ <string name="phoneLabelsGroup">"Téléphone"</string>
+ <string name="emailLabelsGroup">"E-mail"</string>
+ <string name="imLabelsGroup">"Chat"</string>
+ <string name="postalLabelsGroup">"Adresse postale"</string>
+ <string name="otherLabelsGroup">"Autre"</string>
+ <string-array name="otherLabels">
+ <item>"Organisation"</item>
+ <item>"Remarque"</item>
+ </string-array>
+ <string name="errorDialogTitle">"Aucune image"</string>
+ <string name="photoPickerNotFoundText">"Aucune image disponible sur le téléphone."</string>
+ <string name="attachToContact">"Icône de contact"</string>
+ <string name="customLabelPickerTitle">"Libellé personnalisé"</string>
+ <string name="menu_displayGroup">"Afficher les groupes"</string>
+ <string name="syncGroupPreference">"Param. sync. groupes"</string>
+ <string name="importFromSim">"Importer des contacts"</string>
+ <string name="send_to_voicemail_checkbox">"Renvoyer les appels directement vers la boîte vocale."</string>
+ <string name="send_to_voicemail_view">"Les appels sont renvoyés directement vers la boîte vocale."</string>
+ <string name="default_ringtone">"Par défaut"</string>
+ <string name="addPicture">"Ajouter une icône"</string>
+ <string name="removePicture">"Supprimer l\'icône"</string>
+ <string name="noContacts">"Aucun contact."</string>
+ <string name="noContactsWithPhoneNumbers">"Aucun contact disposant d\'un numéro téléphone."</string>
+ <string name="noFavorites">"Aucun favori."</string>
+ <string name="select_group_title">"Groupes"</string>
+ <string name="groupEmpty">"Votre groupe \"<xliff:g id="GROUPNAME">%s</xliff:g>\" est vide."</string>
+ <string name="showAllGroups">"Tous les contacts"</string>
+ <string name="syncAllGroups">"Synchroniser tous les contacts"</string>
+ <string name="groupNameMyContacts">"Mes contacts"</string>
+ <string name="groupNameWithPhones">"Contacts avec des n° de téléphone"</string>
+ <string name="contactCreatedToast">"Contact créé."</string>
+ <string name="contactSavedToast">"Contact enregistré."</string>
+ <string name="listSeparatorCallNumber">"Composer le numéro"</string>
+ <string name="listSeparatorCallNumber_edit">"Numéros de téléphone"</string>
+ <string name="listSeparatorSendSmsMms">"Envoyer un SMS/MMS"</string>
+ <string name="listSeparatorSendEmail">"Envoyer un e-mail"</string>
+ <string name="listSeparatorSendEmail_edit">"Adresses e-mail"</string>
+ <string name="listSeparatorSendIm">"Envoyer un message instantané"</string>
+ <string name="listSeparatorSendIm_edit">"Adresses de chat"</string>
+ <string name="listSeparatorMapAddress">"Adresse sur un plan"</string>
+ <string name="listSeparatorMapAddress_edit">"Adresses postales"</string>
+ <string name="listSeparatorOrganizations">"Organisations"</string>
+ <string name="listSeparatorOtherInformation">"Autres informations"</string>
+ <string name="listSeparatorOtherInformation_edit">"Autres options"</string>
+ <string name="listSeparatorMore_edit">"Plus"</string>
+ <string name="contactsIconLabel">"Contacts"</string>
+ <string name="contactsFavoritesLabel">"Favoris"</string>
+ <string name="dialerIconLabel">"Appeler"</string>
+ <string name="recentCallsIconLabel">"Appels"</string>
+ <string name="liveFolderAll">"Tous les contacts"</string>
+ <string name="liveFolderFavorites">"Contacts suivis"</string>
+ <string name="liveFolderPhone">"Contacts avec numéro de téléphone"</string>
+ <string name="menu_sendTextMessage">"Envoyer un message SMS"</string>
+ <string name="recentCalls_callNumber">"Appeler <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="recentCalls_editNumberBeforeCall">"Modifier le numéro avant d\'effectuer l\'appel"</string>
+ <string name="recentCalls_addToContact">"Ajouter aux contacts"</string>
+ <string name="recentCalls_removeFromRecentList">"Supprimer de la liste des appels"</string>
+ <string name="recentCalls_deleteAll">"Effacer tous les appels"</string>
+ <string name="recentCalls_empty">"Aucun appel."</string>
+ <string name="imei">"IMEI"</string>
+ <string name="voicemail">"Boîte vocale"</string>
+ <string name="unknown">"Inconnu"</string>
+ <string name="private_num">"Numéro privée"</string>
+ <string name="dialerKeyboardHintText">"Utilisez le clavier pour composer le numéro"</string>
+ <string name="dialerDialpadHintText">"Composer le numéro pour ajouter un appel"</string>
+ <string name="simContacts_emptyLoading">"Chargement depuis la carte SIM..."</string>
+ <string name="simContacts_title">"Contacts de carte SIM"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"Synchronisez vos contacts Google !"</font>" "\n"Vos contacts seront disponibles, où que vous soyez, dès que vous aurez synchronisé votre téléphone."</string>
+ <string name="noContactsHelpText">"Aucun contact."\n\n"Pour ajouter des contacts, appuyez sur "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" et sélectionnez :"\n" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un tout nouveau contact"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer des contacts"</b></font>" pour ajouter des contacts à partir de votre carte SIM"\n</li></string>
+ <string name="noContactsHelpTextWithSync">"Aucun contact."\n\n"Pour ajouter des contacts, appuyez sur "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" et sélectionnez :"\n" "\n<li><font fgcolor="#ffffffff"><b>"Param. sync. groupes"</b></font>" pour ajouter des contacts depuis un nouveau compte Google ou un compte existant."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un tout nouveau contact"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer des contacts"</b></font>" pour ajouter des contacts à partir de votre carte SIM"\n</li></string>
+ <string name="seclectSyncGroups_title">"Sélectionner les groupes à synchroniser"</string>
+ <string name="liveFolder_all_label">"Tous les contacts"</string>
+ <string name="liveFolder_favorites_label">"Suivis"</string>
+ <string name="liveFolder_phones_label">"Téléphones"</string>
+ <string name="dialer_useDtmfDialpad">"Utiliser le clavier DTMF"</string>
+ <string name="dialer_returnToInCallScreen">"Reprendre l\'appel en cours"</string>
+ <string name="dialer_addAnotherCall">"Ajouter un appel"</string>
+ <string name="callDetailTitle">"Détails sur l\'appel"</string>
+ <string name="toast_call_detail_error">"Impossible de consulter les détails de l\'appel demandé."</string>
+ <string name="call_type">"Type"</string>
+ <string name="type_incoming">"Appel entrant"</string>
+ <string name="type_outgoing">"Appel sortant"</string>
+ <string name="type_missed">"Appel manqué"</string>
+ <string name="call_time">"Heure"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g> (<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"Durée"</string>
+ <string name="header_actions">"Actions"</string>
+ <string name="ringtone_spinner">"Sonnerie : <xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"Ajouter d\'autres éléments"</string>
+ <string name="actionCall">"Appeler <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionEmail">"Envoyer un e-mail à <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionText">"Envoyer un SMS à <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionChat">"Chatter à l\'aide de <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionMap">"Afficher l\'adresse <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionIncomingCall">"Appels entrants"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"Seront envoyés directement en tant que message vocal"</string>
+ <string name="detailsRingtone">"Associer à <xliff:g id="RINGTONE_NAME">%s</xliff:g>"</string>
+ <string name="callBack">"Rappeler"</string>
+ <string name="callAgain">"Rappeler"</string>
+ <string name="returnCall">"Rappeler"</string>
+ <string name="callDetailsDurationFormat">"<xliff:g id="MINUTES">%s</xliff:g> mn <xliff:g id="SECONDS">%s</xliff:g> s"</string>
+</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
new file mode 100644
index 000000000..59064f039
--- /dev/null
+++ b/res/values-it/strings.xml
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"Contatti"</string>
+ <string name="launcherDialer">"Telefono"</string>
+ <string name="shortcutContact">"Contatto"</string>
+ <string name="starredList">"Speciali"</string>
+ <string name="frequentList">"Frequenti"</string>
+ <string name="strequentList">"Preferiti"</string>
+ <string name="viewContactTitle">"Dettagli contatto"</string>
+ <string name="viewContactDesription">"Visualizza contatto"</string>
+ <string name="editContactDescription">"Modifica contatto"</string>
+ <string name="insertContactDescription">"Crea contatto"</string>
+ <string name="searchHint">"Cerca contatti"</string>
+ <string name="menu_newContact">"Nuovo contatto"</string>
+ <string name="menu_viewContact">"Visualizza contatto"</string>
+ <string name="menu_callNumber">"Chiama <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="menu_addStar">"Aggiungi a preferiti"</string>
+ <string name="menu_removeStar">"Rimuovi dai preferiti"</string>
+ <string name="menu_showBarcode">"Mostra codice a barre"</string>
+ <string name="menu_editContact">"Modifica contatto"</string>
+ <string name="menu_deleteContact">"Elimina contatto"</string>
+ <string name="menu_call">"Chiama"</string>
+ <string name="menu_sendSMS">"Invia SMS/MMS"</string>
+ <string name="menu_sendEmail">"Invia email"</string>
+ <string name="menu_viewAddress">"Indirizzo su mappa"</string>
+ <string name="menu_makeDefaultNumber">"Rendi numero predefinito"</string>
+ <string name="deleteConfirmation_title">"Elimina"</string>
+ <string name="deleteConfirmation">"Il contatto verrà eliminato."</string>
+ <string name="menu_done">"Salva"</string>
+ <string name="menu_doNotSave">"Annulla"</string>
+ <string name="editContact_title_edit">"Modifica contatto"</string>
+ <string name="editContact_title_insert">"Nuovo contatto"</string>
+ <string name="menu_addItem">"Altre info"</string>
+ <string name="label_phonetic_name">"Fonetica"</string>
+ <string name="label_notes">"Note"</string>
+ <string name="label_ringtone">"Suoneria"</string>
+ <string name="ghostData_name">"Nome e cognome"</string>
+ <string name="ghostData_phonetic_name">"Nome fonetico"</string>
+ <string name="ghostData_company">"Società"</string>
+ <string name="ghostData_title">"Mansione"</string>
+ <string name="ghostData_im">"Nome account chat"</string>
+ <string name="ghostData_notes">"La mia nota"</string>
+ <string name="ghostData_phone">"Numero di telefono"</string>
+ <string name="ghostData_email">"Indirizzo email"</string>
+ <string name="ghostData_postal">"Indirizzo postale"</string>
+ <string name="invalidContactMessage">"Il contatto non esiste."</string>
+ <string name="pickerNewContactHeader">"Crea nuovo contatto"</string>
+ <string name="selectLabel">"Seleziona etichetta"</string>
+ <string name="phoneLabelsGroup">"Telefono"</string>
+ <string name="emailLabelsGroup">"Email"</string>
+ <string name="imLabelsGroup">"Chat"</string>
+ <string name="postalLabelsGroup">"Indirizzo postale"</string>
+ <string name="otherLabelsGroup">"Altro"</string>
+ <string-array name="otherLabels">
+ <item>"Organizzazione"</item>
+ <item>"Nota"</item>
+ </string-array>
+ <string name="errorDialogTitle">"Nessuna foto"</string>
+ <string name="photoPickerNotFoundText">"Nessuna foto disponibile."</string>
+ <string name="attachToContact">"Icona del contatto"</string>
+ <string name="customLabelPickerTitle">"Nome etichetta personalizzata"</string>
+ <string name="menu_displayGroup">"Visualizza gruppo"</string>
+ <string name="syncGroupPreference">"Modifica sincr. gruppi"</string>
+ <string name="importFromSim">"Importa contatti"</string>
+ <string name="send_to_voicemail_checkbox">"Inoltra chiamate direttamente alla segreteria"</string>
+ <string name="send_to_voicemail_view">"Le chiamate vengono inoltrate direttamente alla segreteria."</string>
+ <string name="default_ringtone">"Predefinita"</string>
+ <string name="addPicture">"Aggiungi icona"</string>
+ <string name="removePicture">"Rimuovi icona"</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="select_group_title">"Gruppi"</string>
+ <string name="groupEmpty">"Il gruppo \"<xliff:g id="GROUPNAME">%s</xliff:g>\" è vuoto."</string>
+ <string name="showAllGroups">"Tutti i contatti"</string>
+ <string name="syncAllGroups">"Sincronizza tutti i contatti"</string>
+ <string name="groupNameMyContacts">"I miei contatti"</string>
+ <string name="groupNameWithPhones">"Contatti con numeri di telefono"</string>
+ <string name="contactCreatedToast">"Contatto creato."</string>
+ <string name="contactSavedToast">"Contatto salvato."</string>
+ <string name="listSeparatorCallNumber">"Componi numero"</string>
+ <string name="listSeparatorCallNumber_edit">"Numeri di telefono"</string>
+ <string name="listSeparatorSendSmsMms">"Invia SMS/MMS"</string>
+ <string name="listSeparatorSendEmail">"Invia email"</string>
+ <string name="listSeparatorSendEmail_edit">"Indirizzi email"</string>
+ <string name="listSeparatorSendIm">"Invia messagio chat"</string>
+ <string name="listSeparatorSendIm_edit">"Indirizzi chat"</string>
+ <string name="listSeparatorMapAddress">"Indirizzo su mappa"</string>
+ <string name="listSeparatorMapAddress_edit">"Indirizzi postali"</string>
+ <string name="listSeparatorOrganizations">"Organizzazioni"</string>
+ <string name="listSeparatorOtherInformation">"Altre informazioni"</string>
+ <string name="listSeparatorOtherInformation_edit">"Altre opzioni"</string>
+ <string name="listSeparatorMore_edit">"Altro"</string>
+ <string name="contactsIconLabel">"Contatti"</string>
+ <string name="contactsFavoritesLabel">"Preferiti"</string>
+ <string name="dialerIconLabel">"Telefono"</string>
+ <string name="recentCallsIconLabel">"Reg. chiamate"</string>
+ <string name="liveFolderAll">"Tutti i contatti"</string>
+ <string name="liveFolderFavorites">"Contatti speciali"</string>
+ <string name="liveFolderPhone">"Contatti con numeri di telefono"</string>
+ <string name="menu_sendTextMessage">"Invia SMS"</string>
+ <string name="recentCalls_callNumber">"Chiama <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="recentCalls_editNumberBeforeCall">"Modifica prima di chiamare"</string>
+ <string name="recentCalls_addToContact">"Aggiungi a contatti"</string>
+ <string name="recentCalls_removeFromRecentList">"Rimuovi da registro"</string>
+ <string name="recentCalls_deleteAll">"Cancella registro chiamate"</string>
+ <string name="recentCalls_empty">"Il registro chiamate è vuoto."</string>
+ <string name="imei">"IMEI"</string>
+ <string name="voicemail">"Segreteria"</string>
+ <string name="unknown">"Sconosciuto"</string>
+ <string name="private_num">"Numero privato"</string>
+ <string name="dialerKeyboardHintText">"Usa la tastiera"</string>
+ <string name="dialerDialpadHintText">"Componi per aggiungere una chiamata"</string>
+ <string name="simContacts_emptyLoading">"Caricamento da SIM..."</string>
+ <string name="simContacts_title">"Contatti SIM"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"Sincronizza i tuoi contatti Google."</font>" "\n"Dopo la sincronizzazione con il telefono, i tuoi contatti saranno sempre a tua disposizione."</string>
+ <string name="noContactsHelpText">"Non hai nessun contatto."\n\n"Per aggiungere dei contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e seleziona:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa contatti"</b></font>" per aggiungere i contatti salvati sulla scheda SIM card"\n</li></string>
+ <string name="noContactsHelpTextWithSync">"Non hai nessun contatto."\n\n"Per aggiungere i contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e seleziona:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Modifica sincr. gruppi"</b></font>" per aggiungere contatti da un account Google nuovo o già esistente"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare un contatto nuovo"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa contatti"</b></font>" per aggiungere i contatti salvati sulla scheda SIM"\n</li></string>
+ <string name="seclectSyncGroups_title">"Seleziona i gruppi da sincronizzare"</string>
+ <string name="liveFolder_all_label">"Tutti i contatti"</string>
+ <string name="liveFolder_favorites_label">"Speciali"</string>
+ <string name="liveFolder_phones_label">"Telefoni"</string>
+ <string name="dialer_useDtmfDialpad">"Usa tastierino per selezione a toni"</string>
+ <string name="dialer_returnToInCallScreen">"Torna alla chiamata in corso"</string>
+ <string name="dialer_addAnotherCall">"Aggiungi"</string>
+ <string name="callDetailTitle">"Dettagli chiamata"</string>
+ <string name="toast_call_detail_error">"Impossibile leggere i dettagli per la chiamata richiesta."</string>
+ <string name="call_type">"Tipo"</string>
+ <string name="type_incoming">"Chiamata in arrivo"</string>
+ <string name="type_outgoing">"Chiamata in uscita"</string>
+ <string name="type_missed">"Chiamata persa"</string>
+ <string name="call_time">"Ora"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g> (<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"Durata"</string>
+ <string name="header_actions">"Azioni"</string>
+ <string name="ringtone_spinner">"Suoneria: <xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"Aggiungi altri elementi"</string>
+ <string name="actionCall">"Chiama n. <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionEmail">"Invia email a ind. <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionText">"Invia SMS a n. <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionChat">"Chatta su <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionMap">"Visualizza indirizzo <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionIncomingCall">"Chiamate in arrivo"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"Il messaggio verrà inviato direttamente alla segreteria"</string>
+ <string name="detailsRingtone">"Impostato su <xliff:g id="RINGTONE_NAME">%s</xliff:g>"</string>
+ <string name="callBack">"Richiama"</string>
+ <string name="callAgain">"Richiama"</string>
+ <string name="returnCall">"Chiama numero"</string>
+ <string name="callDetailsDurationFormat">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> sec"</string>
+</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
new file mode 100644
index 000000000..117c01558
--- /dev/null
+++ b/res/values-ja/strings.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"連絡先"</string>
+ <!-- no translation found for launcherDialer (140610573639849799) -->
+ <skip />
+ <string name="shortcutContact">"連絡先"</string>
+ <string name="starredList">"スター付き"</string>
+ <string name="frequentList">"よく連絡する連絡先"</string>
+ <string name="strequentList">"お気に入り"</string>
+ <string name="viewContactTitle">"連絡先情報"</string>
+ <string name="viewContactDesription">"連絡先を表示"</string>
+ <string name="editContactDescription">"連絡先を編集"</string>
+ <string name="insertContactDescription">"連絡先を新規登録"</string>
+ <string name="searchHint">"連絡先を検索"</string>
+ <string name="menu_newContact">"連絡先を新規登録"</string>
+ <string name="menu_viewContact">"連絡先を表示"</string>
+ <string name="menu_callNumber">"<xliff:g id="NAME">%s</xliff:g>さんに発信"</string>
+ <string name="menu_addStar">"お気に入りに追加"</string>
+ <string name="menu_removeStar">"お気に入りから削除"</string>
+ <string name="menu_showBarcode">"バーコードを表示"</string>
+ <string name="menu_editContact">"連絡先を編集"</string>
+ <string name="menu_deleteContact">"連絡先を削除"</string>
+ <string name="menu_call">"発信"</string>
+ <string name="menu_sendSMS">"SMS/MMSを送信"</string>
+ <string name="menu_sendEmail">"メールを送信"</string>
+ <string name="menu_viewAddress">"地図でみる"</string>
+ <string name="menu_makeDefaultNumber">"メインの番号に設定する"</string>
+ <string name="deleteConfirmation_title">"削除"</string>
+ <string name="deleteConfirmation">"この連絡先を削除します。"</string>
+ <string name="menu_done">"完了"</string>
+ <!-- no translation found for menu_doNotSave (2174577548513895144) -->
+ <skip />
+ <string name="editContact_title_edit">"連絡先を編集"</string>
+ <string name="editContact_title_insert">"連絡先を新規登録"</string>
+ <string name="menu_addItem">"情報を追加"</string>
+ <string name="label_phonetic_name">"音声"</string>
+ <string name="label_notes">"メモ"</string>
+ <string name="label_ringtone">"着信音"</string>
+ <string name="ghostData_name">"名前"</string>
+ <string name="ghostData_phonetic_name">"音声名"</string>
+ <string name="ghostData_company">"会社"</string>
+ <string name="ghostData_title">"役職"</string>
+ <string name="ghostData_im">"IM名"</string>
+ <string name="ghostData_notes">"メモ"</string>
+ <string name="ghostData_phone">"電話番号"</string>
+ <string name="ghostData_email">"メールアドレス"</string>
+ <string name="ghostData_postal">"住所"</string>
+ <string name="invalidContactMessage">"この連絡先は消去されています。"</string>
+ <string name="pickerNewContactHeader">"連絡先を新規登録"</string>
+ <string name="selectLabel">"選択してください"</string>
+ <string name="phoneLabelsGroup">"電話番号"</string>
+ <string name="emailLabelsGroup">"メール"</string>
+ <string name="imLabelsGroup">"IM"</string>
+ <string name="postalLabelsGroup">"住所"</string>
+ <string name="otherLabelsGroup">"その他"</string>
+ <string-array name="otherLabels">
+ <item>"勤務先情報"</item>
+ <item>"メモ"</item>
+ </string-array>
+ <string name="errorDialogTitle">"写真なし"</string>
+ <string name="photoPickerNotFoundText">"写真がありません。"</string>
+ <string name="attachToContact">"連絡先のアイコン"</string>
+ <string name="customLabelPickerTitle">"新しいラベル名"</string>
+ <string name="menu_displayGroup">"グループを表示"</string>
+ <string name="syncGroupPreference">"同期グループを編集"</string>
+ <string name="importFromSim">"連絡先をインポート"</string>
+ <string name="send_to_voicemail_checkbox">"ボイスメールに直接電話します。"</string>
+ <string name="send_to_voicemail_view">"この番号にかけるとボイスメールに送信されます。"</string>
+ <string name="default_ringtone">"端末既定"</string>
+ <string name="addPicture">"アイコンを追加"</string>
+ <string name="removePicture">"アイコンを削除"</string>
+ <string name="noContacts">"連絡先がありません。"</string>
+ <string name="noContactsWithPhoneNumbers">"電話番号付きの連絡先はありません。"</string>
+ <string name="noFavorites">"お気に入りなし"</string>
+ <string name="select_group_title">"グループ"</string>
+ <string name="groupEmpty">"「<xliff:g id="GROUPNAME">%s</xliff:g>」グループには何も登録されていません。"</string>
+ <string name="showAllGroups">"すべての連絡先"</string>
+ <string name="syncAllGroups">"全連絡先を同期"</string>
+ <string name="groupNameMyContacts">"Myコンタクト"</string>
+ <string name="groupNameWithPhones">"電話番号のある連絡先"</string>
+ <string name="contactCreatedToast">"連絡先を作成しました。"</string>
+ <string name="contactSavedToast">"連絡先を保存しました。"</string>
+ <string name="listSeparatorCallNumber">"電話をかける"</string>
+ <string name="listSeparatorCallNumber_edit">"電話番号"</string>
+ <string name="listSeparatorSendSmsMms">"SMS/MMSを送信"</string>
+ <string name="listSeparatorSendEmail">"メールを送信"</string>
+ <string name="listSeparatorSendEmail_edit">"メールアドレス"</string>
+ <string name="listSeparatorSendIm">"インスタントメッセージを送信"</string>
+ <string name="listSeparatorSendIm_edit">"チャットアドレス"</string>
+ <string name="listSeparatorMapAddress">"地図でみる"</string>
+ <string name="listSeparatorMapAddress_edit">"住所"</string>
+ <string name="listSeparatorOrganizations">"勤務先情報"</string>
+ <string name="listSeparatorOtherInformation">"その他の情報"</string>
+ <string name="listSeparatorOtherInformation_edit">"その他のオプション"</string>
+ <string name="listSeparatorMore_edit">"開く"</string>
+ <string name="contactsIconLabel">"連絡先"</string>
+ <string name="contactsFavoritesLabel">"お気に入り"</string>
+ <string name="dialerIconLabel">"通話"</string>
+ <string name="recentCallsIconLabel">"通話履歴"</string>
+ <string name="liveFolderAll">"すべての連絡先"</string>
+ <string name="liveFolderFavorites">"スター付きの連絡先"</string>
+ <string name="liveFolderPhone">"電話番号のある連絡先"</string>
+ <string name="menu_sendTextMessage">"SMSメッセージを送信"</string>
+ <string name="recentCalls_callNumber">"<xliff:g id="NAME">%s</xliff:g>さんに発信"</string>
+ <string name="recentCalls_editNumberBeforeCall">"発信前に番号を編集"</string>
+ <string name="recentCalls_addToContact">"連絡先に追加"</string>
+ <string name="recentCalls_removeFromRecentList">"通話履歴から消去"</string>
+ <string name="recentCalls_deleteAll">"通話履歴を全件消去"</string>
+ <string name="recentCalls_empty">"通話履歴なし"</string>
+ <string name="imei">"IMEI(端末識別番号)"</string>
+ <string name="voicemail">"ボイスメール"</string>
+ <string name="unknown">"不明"</string>
+ <string name="private_num">"非通知番号"</string>
+ <string name="dialerKeyboardHintText">"キーボードで番号を入力してください"</string>
+ <string name="dialerDialpadHintText">"ダイヤルして追加"</string>
+ <string name="simContacts_emptyLoading">"SIMカードから読み取り中..."</string>
+ <string name="simContacts_title">"SIMカードの連絡先"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"Googleアカウントの連絡先同期"</font>" "\n"Googleサービスで使っている連絡先をこの携帯電話でも使えるようになります。"</string>
+ <!-- no translation found for noContactsHelpText (2249195687463896364) -->
+ <skip />
+ <!-- no translation found for noContactsHelpTextWithSync (4927701496550314555) -->
+ <skip />
+ <string name="seclectSyncGroups_title">"同期するグループを選択"</string>
+ <string name="liveFolder_all_label">"すべての連絡先"</string>
+ <string name="liveFolder_favorites_label">"スター付き"</string>
+ <string name="liveFolder_phones_label">"電話"</string>
+ <string name="dialer_useDtmfDialpad">"プッシュホン式キーパッドを使う"</string>
+ <string name="dialer_returnToInCallScreen">"保留中の通話に戻る"</string>
+ <string name="dialer_addAnotherCall">"別の通話を追加"</string>
+ <string name="callDetailTitle">"通話詳細"</string>
+ <string name="toast_call_detail_error">"通話要求の詳細が読み込めませんでした。"</string>
+ <string name="call_type">"入力"</string>
+ <string name="type_incoming">"通話着信"</string>
+ <string name="type_outgoing">"通話送信"</string>
+ <string name="type_missed">"不在着信"</string>
+ <string name="call_time">"時間"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g> (<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"経過時間"</string>
+ <string name="header_actions">"操作"</string>
+ <string name="ringtone_spinner">"着信音: <xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"その他の項目を追加"</string>
+ <string name="actionCall">"<xliff:g id="TYPE">%s</xliff:g>に発信"</string>
+ <string name="actionEmail">"<xliff:g id="TYPE">%s</xliff:g>にメールを送信"</string>
+ <string name="actionText">"<xliff:g id="TYPE">%s</xliff:g>にテキストを送信"</string>
+ <string name="actionChat">"<xliff:g id="TYPE">%s</xliff:g>を使用してチャット"</string>
+ <string name="actionMap">"<xliff:g id="TYPE">%s</xliff:g>の住所を表示"</string>
+ <string name="actionIncomingCall">"着信"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"直接ボイスメールに送信します"</string>
+ <string name="detailsRingtone">"<xliff:g id="RINGTONE_NAME">%s</xliff:g>に設定"</string>
+ <string name="callBack">"コールバック"</string>
+ <!-- no translation found for callAgain (3197312117049874778) -->
+ <skip />
+ <!-- no translation found for returnCall (8171961914203617813) -->
+ <skip />
+ <!-- no translation found for callDetailsDurationFormat (8157706382818184268) -->
+ <skip />
+</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
new file mode 100644
index 000000000..aafe0d0e0
--- /dev/null
+++ b/res/values-ko/strings.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"연락처"</string>
+ <!-- no translation found for launcherDialer (140610573639849799) -->
+ <skip />
+ <string name="shortcutContact">"연락처"</string>
+ <string name="starredList">"별표 표시된 연락처"</string>
+ <string name="frequentList">"자주 거는 전화"</string>
+ <string name="strequentList">"자주 거는 전화"</string>
+ <string name="viewContactTitle">"연락처 상세정보"</string>
+ <string name="viewContactDesription">"연락처 보기"</string>
+ <string name="editContactDescription">"연락처 편집"</string>
+ <string name="insertContactDescription">"연락처 만들기"</string>
+ <string name="searchHint">"연락처 검색"</string>
+ <string name="menu_newContact">"새 연락처"</string>
+ <string name="menu_viewContact">"연락처 보기"</string>
+ <string name="menu_callNumber">"<xliff:g id="NAME">%s</xliff:g>에게 전화걸기"</string>
+ <string name="menu_addStar">"즐겨찾기에 추가"</string>
+ <string name="menu_removeStar">"즐겨찾기에서 삭제"</string>
+ <string name="menu_showBarcode">"바코드 표시"</string>
+ <string name="menu_editContact">"연락처 편집"</string>
+ <string name="menu_deleteContact">"연락처 삭제"</string>
+ <string name="menu_call">"전화걸기"</string>
+ <string name="menu_sendSMS">"SMS/MMS 보내기"</string>
+ <string name="menu_sendEmail">"이메일 보내기"</string>
+ <string name="menu_viewAddress">"지도상의 주소"</string>
+ <string name="menu_makeDefaultNumber">"기본 번호로 설정"</string>
+ <string name="deleteConfirmation_title">"삭제"</string>
+ <string name="deleteConfirmation">"연락처가 삭제됩니다."</string>
+ <string name="menu_done">"완료"</string>
+ <!-- no translation found for menu_doNotSave (2174577548513895144) -->
+ <skip />
+ <string name="editContact_title_edit">"연락처 편집"</string>
+ <string name="editContact_title_insert">"새 연락처"</string>
+ <string name="menu_addItem">"추가정보"</string>
+ <string name="label_phonetic_name">"소리나는 대로"</string>
+ <string name="label_notes">"메모"</string>
+ <string name="label_ringtone">"벨소리"</string>
+ <string name="ghostData_name">"이름"</string>
+ <string name="ghostData_phonetic_name">"소리나는 대로의 이름"</string>
+ <string name="ghostData_company">"회사"</string>
+ <string name="ghostData_title">"직급"</string>
+ <string name="ghostData_im">"IM 이름"</string>
+ <string name="ghostData_notes">"내 메모"</string>
+ <string name="ghostData_phone">"전화번호"</string>
+ <string name="ghostData_email">"이메일 주소"</string>
+ <string name="ghostData_postal">"주소"</string>
+ <string name="invalidContactMessage">"연락처가 없습니다."</string>
+ <string name="pickerNewContactHeader">"새 연락처 만들기"</string>
+ <string name="selectLabel">"라벨 선택"</string>
+ <string name="phoneLabelsGroup">"전화"</string>
+ <string name="emailLabelsGroup">"이메일"</string>
+ <string name="imLabelsGroup">"IM"</string>
+ <string name="postalLabelsGroup">"주소"</string>
+ <string name="otherLabelsGroup">"기타"</string>
+ <string-array name="otherLabels">
+ <item>"조직"</item>
+ <item>"메모"</item>
+ </string-array>
+ <string name="errorDialogTitle">"사진 없음"</string>
+ <string name="photoPickerNotFoundText">"전화기에 사진이 없습니다."</string>
+ <string name="attachToContact">"연락처 아이콘"</string>
+ <string name="customLabelPickerTitle">"맞춤 라벨 이름"</string>
+ <string name="menu_displayGroup">"그룹 표시"</string>
+ <string name="syncGroupPreference">"동기화 그룹 편집"</string>
+ <string name="importFromSim">"연락처 가져오기"</string>
+ <string name="send_to_voicemail_checkbox">"음성메일에 직접 전화걸기"</string>
+ <string name="send_to_voicemail_view">"수신전화가 바로 음성메일로 연결됩니다."</string>
+ <string name="default_ringtone">"기본 벨소리"</string>
+ <string name="addPicture">"사진 추가"</string>
+ <string name="removePicture">"사진 제거"</string>
+ <string name="noContacts">"연락처가 없습니다."</string>
+ <string name="noContactsWithPhoneNumbers">"전화번호가 포함된 연락처가 없습니다."</string>
+ <string name="noFavorites">"자주 거는 전화가 없습니다."</string>
+ <string name="select_group_title">"그룹"</string>
+ <string name="groupEmpty">"\"<xliff:g id="GROUPNAME">%s</xliff:g>\" 그룹이 비었습니다."</string>
+ <string name="showAllGroups">"모든 연락처"</string>
+ <string name="syncAllGroups">"모든 연락처 동기화"</string>
+ <string name="groupNameMyContacts">"내 연락처"</string>
+ <string name="groupNameWithPhones">"전화번호가 포함된 연락처"</string>
+ <string name="contactCreatedToast">"연락처가 생성되었습니다."</string>
+ <string name="contactSavedToast">"연락처가 저장되었습니다."</string>
+ <string name="listSeparatorCallNumber">"전화번호"</string>
+ <string name="listSeparatorCallNumber_edit">"전화번호"</string>
+ <string name="listSeparatorSendSmsMms">"SMS/MMS 보내기"</string>
+ <string name="listSeparatorSendEmail">"이메일 보내기"</string>
+ <string name="listSeparatorSendEmail_edit">"이메일 주소"</string>
+ <string name="listSeparatorSendIm">"인스턴트 메시지 보내기"</string>
+ <string name="listSeparatorSendIm_edit">"채팅 주소"</string>
+ <string name="listSeparatorMapAddress">"지도상의 주소"</string>
+ <string name="listSeparatorMapAddress_edit">"우편 주소"</string>
+ <string name="listSeparatorOrganizations">"조직"</string>
+ <string name="listSeparatorOtherInformation">"기타 정보"</string>
+ <string name="listSeparatorOtherInformation_edit">"기타 옵션"</string>
+ <string name="listSeparatorMore_edit">"더보기"</string>
+ <string name="contactsIconLabel">"연락처"</string>
+ <string name="contactsFavoritesLabel">"자주 거는 전화"</string>
+ <string name="dialerIconLabel">"전화걸기"</string>
+ <string name="recentCallsIconLabel">"통화기록"</string>
+ <string name="liveFolderAll">"전체 주소록"</string>
+ <string name="liveFolderFavorites">"별표 표시된 주소"</string>
+ <string name="liveFolderPhone">"전화번호 주소록"</string>
+ <string name="menu_sendTextMessage">"SMS 메시지 보내기"</string>
+ <string name="recentCalls_callNumber">"<xliff:g id="NAME">%s</xliff:g>에게 전화걸기"</string>
+ <string name="recentCalls_editNumberBeforeCall">"전화 걸기 전에 번호 편집"</string>
+ <string name="recentCalls_addToContact">"연락처에 추가"</string>
+ <string name="recentCalls_removeFromRecentList">"통화기록에서 제거"</string>
+ <string name="recentCalls_deleteAll">"통화기록 지우기"</string>
+ <string name="recentCalls_empty">"통화기록이 없습니다."</string>
+ <string name="imei">"IMEI"</string>
+ <string name="voicemail">"음성메일"</string>
+ <string name="unknown">"알 수 없음"</string>
+ <string name="private_num">"비공개 번호"</string>
+ <string name="dialerKeyboardHintText">"키보드를 사용하여 전화걸기"</string>
+ <string name="dialerDialpadHintText">"통화를 추가할 전화번호 입력"</string>
+ <string name="simContacts_emptyLoading">"SIM 카드에서 로드 중…"</string>
+ <string name="simContacts_title">"SIM 카드 연락처"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"Google 연락처를 동기화하세요."</font>" "\n"연락처를 전화기로 동기화하면 어디에서나 연락처를 확인할 수 있습니다."</string>
+ <!-- no translation found for noContactsHelpText (2249195687463896364) -->
+ <skip />
+ <!-- no translation found for noContactsHelpTextWithSync (4927701496550314555) -->
+ <skip />
+ <string name="seclectSyncGroups_title">"동기화할 그룹 선택"</string>
+ <string name="liveFolder_all_label">"전체 주소록"</string>
+ <string name="liveFolder_favorites_label">"중요편지함"</string>
+ <string name="liveFolder_phones_label">"전화번호"</string>
+ <string name="dialer_useDtmfDialpad">"터치톤 키패드 사용"</string>
+ <string name="dialer_returnToInCallScreen">"진행 중인 통화로 돌아가기"</string>
+ <string name="dialer_addAnotherCall">"통화 추가"</string>
+ <string name="callDetailTitle">"통화 세부정보"</string>
+ <string name="toast_call_detail_error">"요청한 통화의 세부정보를 볼 수 없습니다."</string>
+ <string name="call_type">"유형"</string>
+ <string name="type_incoming">"수신통화"</string>
+ <string name="type_outgoing">"발신통화"</string>
+ <string name="type_missed">"부재중 전화"</string>
+ <string name="call_time">"시간"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g>(<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"통화시간"</string>
+ <string name="header_actions">"작업"</string>
+ <string name="ringtone_spinner">"벨소리: <xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"항목 더 추가"</string>
+ <string name="actionCall">"<xliff:g id="TYPE">%s</xliff:g>(으)로 전화걸기"</string>
+ <string name="actionEmail">"<xliff:g id="TYPE">%s</xliff:g>(으)로 이메일 보내기"</string>
+ <string name="actionText">"<xliff:g id="TYPE">%s</xliff:g>(으)로 문자 보내기"</string>
+ <string name="actionChat">"<xliff:g id="TYPE">%s</xliff:g> 채팅"</string>
+ <string name="actionMap">"<xliff:g id="TYPE">%s</xliff:g> 주소 보기"</string>
+ <string name="actionIncomingCall">"수신 전화"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"음성메일로 바로 보냄"</string>
+ <string name="detailsRingtone">"<xliff:g id="RINGTONE_NAME">%s</xliff:g>(으)로 설정"</string>
+ <string name="callBack">"콜백"</string>
+ <!-- no translation found for callAgain (3197312117049874778) -->
+ <skip />
+ <!-- no translation found for returnCall (8171961914203617813) -->
+ <skip />
+ <!-- no translation found for callDetailsDurationFormat (8157706382818184268) -->
+ <skip />
+</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
new file mode 100644
index 000000000..3b47bac03
--- /dev/null
+++ b/res/values-nb/strings.xml
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"Kontakter"</string>
+ <string name="launcherDialer">"Telefon"</string>
+ <string name="shortcutContact">"Kontakt"</string>
+ <string name="starredList">"Med stjerne"</string>
+ <string name="frequentList">"Mest brukt"</string>
+ <string name="strequentList">"Favoritter"</string>
+ <string name="viewContactTitle">"Egenskaper for kontakt"</string>
+ <string name="viewContactDesription">"Se på kontakt"</string>
+ <string name="editContactDescription">"Rediger kontakt"</string>
+ <string name="insertContactDescription">"Opprett kontakt"</string>
+ <string name="searchHint">"Søk i kontakter"</string>
+ <string name="menu_newContact">"Ny kontakt"</string>
+ <string name="menu_viewContact">"Se på kontakt"</string>
+ <string name="menu_callNumber">"Ring <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="menu_addStar">"Legg til som favoritt"</string>
+ <string name="menu_removeStar">"Fjern fra favoritter"</string>
+ <string name="menu_showBarcode">"Vis strekkode"</string>
+ <string name="menu_editContact">"Rediger kontakt"</string>
+ <string name="menu_deleteContact">"Slett kontakt"</string>
+ <string name="menu_call">"Ring"</string>
+ <string name="menu_sendSMS">"Send SMS/MMS"</string>
+ <string name="menu_sendEmail">"Send e-post"</string>
+ <string name="menu_viewAddress">"Se i kart"</string>
+ <string name="menu_makeDefaultNumber">"Gjør til foretrukket nummer"</string>
+ <string name="deleteConfirmation_title">"Slett"</string>
+ <string name="deleteConfirmation">"Denne kontakten vil bli slettet."</string>
+ <string name="menu_done">"Lagre"</string>
+ <!-- no translation found for menu_doNotSave (2174577548513895144) -->
+ <skip />
+ <string name="editContact_title_edit">"Rediger kontakt"</string>
+ <string name="editContact_title_insert">"Ny kontakt"</string>
+ <string name="menu_addItem">"Mer informasjon"</string>
+ <string name="label_phonetic_name">"Fonetisk"</string>
+ <string name="label_notes">"Notater"</string>
+ <string name="label_ringtone">"Ringetone"</string>
+ <string name="ghostData_name">"For- og etternavn"</string>
+ <string name="ghostData_phonetic_name">"Fonetisk navn"</string>
+ <string name="ghostData_company">"Firma"</string>
+ <string name="ghostData_title">"Stilling"</string>
+ <string name="ghostData_im">"Lynmeldingsnavn"</string>
+ <string name="ghostData_notes">"Mine notater"</string>
+ <string name="ghostData_phone">"Telefonnummer"</string>
+ <string name="ghostData_email">"E-postadresse"</string>
+ <string name="ghostData_postal">"Postadresse"</string>
+ <string name="invalidContactMessage">"Kontakten finnes ikke."</string>
+ <string name="pickerNewContactHeader">"Opprett ny kontakt"</string>
+ <string name="selectLabel">"Velg etikett"</string>
+ <string name="phoneLabelsGroup">"Telefon"</string>
+ <string name="emailLabelsGroup">"E-post"</string>
+ <string name="imLabelsGroup">"Lynmeldinger"</string>
+ <string name="postalLabelsGroup">"Postadresse"</string>
+ <string name="otherLabelsGroup">"Annet"</string>
+ <string-array name="otherLabels">
+ <item>"Organisasjon"</item>
+ <item>"Notat"</item>
+ </string-array>
+ <string name="errorDialogTitle">"Ingen bilder"</string>
+ <string name="photoPickerNotFoundText">"Det er ingen bilder på telefonen."</string>
+ <string name="attachToContact">"Kontaktikon"</string>
+ <string name="customLabelPickerTitle">"Egendefinert etikett"</string>
+ <string name="menu_displayGroup">"Vis gruppe"</string>
+ <string name="syncGroupPreference">"Rediger synkroniserte grupper"</string>
+ <string name="importFromSim">"Importer kontakter"</string>
+ <string name="send_to_voicemail_checkbox">"Send anrop direkte til telefonsvarer."</string>
+ <string name="send_to_voicemail_view">"Anrop blir sendt direkte til telefonsvarer."</string>
+ <string name="default_ringtone">"Standardvalg"</string>
+ <string name="addPicture">"Legg til ikon"</string>
+ <string name="removePicture">"Fjern ikon"</string>
+ <string name="noContacts">"Ingen kontakter."</string>
+ <string name="noContactsWithPhoneNumbers">"Ingen kontakter med telefonnummer."</string>
+ <string name="noFavorites">"Ingen favoritter."</string>
+ <string name="select_group_title">"Grupper"</string>
+ <string name="groupEmpty">"Gruppen \"<xliff:g id="GROUPNAME">%s</xliff:g>\" er tom."</string>
+ <string name="showAllGroups">"Alle kontakter"</string>
+ <string name="syncAllGroups">"Synkroniser alle kontakter"</string>
+ <string name="groupNameMyContacts">"Mine kontakter"</string>
+ <string name="groupNameWithPhones">"Kontakter med telefonnummer"</string>
+ <string name="contactCreatedToast">"Kontakt opprettet."</string>
+ <string name="contactSavedToast">"Kontakt lagret."</string>
+ <string name="listSeparatorCallNumber">"Ring nummer"</string>
+ <string name="listSeparatorCallNumber_edit">"Telefonnummer"</string>
+ <string name="listSeparatorSendSmsMms">"Send SMS/MMS"</string>
+ <string name="listSeparatorSendEmail">"Send e-post"</string>
+ <string name="listSeparatorSendEmail_edit">"E-postadresser"</string>
+ <string name="listSeparatorSendIm">"Send lynmelding"</string>
+ <string name="listSeparatorSendIm_edit">"Prateadresser"</string>
+ <string name="listSeparatorMapAddress">"Se i kart"</string>
+ <string name="listSeparatorMapAddress_edit">"Postadresser"</string>
+ <string name="listSeparatorOrganizations">"Organisasjoner"</string>
+ <string name="listSeparatorOtherInformation">"Annen informasjon"</string>
+ <string name="listSeparatorOtherInformation_edit">"Andre alternativer"</string>
+ <string name="listSeparatorMore_edit">"Mer"</string>
+ <string name="contactsIconLabel">"Alle"</string>
+ <string name="contactsFavoritesLabel">"Vanlige"</string>
+ <string name="dialerIconLabel">"Telefon"</string>
+ <string name="recentCallsIconLabel">"Logg"</string>
+ <string name="liveFolderAll">"Alle kontakter"</string>
+ <string name="liveFolderFavorites">"Kontakter med stjerne"</string>
+ <string name="liveFolderPhone">"Kontakter med telefonnummer"</string>
+ <string name="menu_sendTextMessage">"Send SMS-melding"</string>
+ <string name="recentCalls_callNumber">"Ring <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="recentCalls_editNumberBeforeCall">"Rediger nummer før anrop"</string>
+ <string name="recentCalls_addToContact">"Legg til kontakter"</string>
+ <string name="recentCalls_removeFromRecentList">"Fjern fra anropslogg"</string>
+ <string name="recentCalls_deleteAll">"Nullstill anropslogg"</string>
+ <string name="recentCalls_empty">"Anropsloggen er tom."</string>
+ <string name="imei">"IMEI"</string>
+ <string name="voicemail">"Telefonsvarer"</string>
+ <string name="unknown">"Ukjent"</string>
+ <string name="private_num">"Skjult nummer"</string>
+ <string name="dialerKeyboardHintText">"Bruk tastaturet for å ringe"</string>
+ <string name="dialerDialpadHintText">"Ring for å legge til en samtale"</string>
+ <string name="simContacts_emptyLoading">"Henter fra SIM-kort…"</string>
+ <string name="simContacts_title">"Kontakter på SIM-kort"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"Synkroniser Google-kontaktene dine!"</font>" "\n"Etter å ha synkronisert telefonen vil kontaktene dine bli tilgjengelig overalt."</string>
+ <!-- no translation found for noContactsHelpText (2249195687463896364) -->
+ <skip />
+ <!-- no translation found for noContactsHelpTextWithSync (4927701496550314555) -->
+ <skip />
+ <string name="seclectSyncGroups_title">"Velg grupper som skal synkroniseres"</string>
+ <string name="liveFolder_all_label">"Alle kontakter"</string>
+ <string name="liveFolder_favorites_label">"Med stjerne"</string>
+ <string name="liveFolder_phones_label">"Telefoner"</string>
+ <string name="dialer_useDtmfDialpad">"Bruk tonetastatur"</string>
+ <string name="dialer_returnToInCallScreen">"Gå tilbake til pågående samtale"</string>
+ <string name="dialer_addAnotherCall">"Legg til en samtale"</string>
+ <string name="callDetailTitle">"Samtaleinformasjon"</string>
+ <string name="toast_call_detail_error">"Kunne ikke lese informasjon for forespurt samtale."</string>
+ <string name="call_type">"Type"</string>
+ <string name="type_incoming">"Innkommende samtale"</string>
+ <string name="type_outgoing">"Utgående samtale"</string>
+ <string name="type_missed">"Tapt anrop"</string>
+ <string name="call_time">"Tid"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g> (<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"Varighet"</string>
+ <string name="header_actions">"Handlinger"</string>
+ <string name="ringtone_spinner">"Ringetone: <xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"Legg til flere elementer"</string>
+ <string name="actionCall">"Ring <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionEmail">"Send e-post til <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionText">"Send SMS til <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionChat">"Prat over <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionMap">"Vis <xliff:g id="TYPE">%s</xliff:g>adresse"</string>
+ <string name="actionIncomingCall">"Innkommende samtaler"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"Vil bli sendt rett til telefonsvarer"</string>
+ <string name="detailsRingtone">"Sett til <xliff:g id="RINGTONE_NAME">%s</xliff:g>"</string>
+ <string name="callBack">"Ring tilbake"</string>
+ <!-- no translation found for callAgain (3197312117049874778) -->
+ <skip />
+ <!-- no translation found for returnCall (8171961914203617813) -->
+ <skip />
+ <!-- no translation found for callDetailsDurationFormat (8157706382818184268) -->
+ <skip />
+</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
new file mode 100644
index 000000000..0d3a5569f
--- /dev/null
+++ b/res/values-nl/strings.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"Contacten"</string>
+ <!-- no translation found for launcherDialer (140610573639849799) -->
+ <skip />
+ <string name="shortcutContact">"Contacten"</string>
+ <string name="starredList">"Met ster"</string>
+ <string name="frequentList">"Vaak"</string>
+ <string name="strequentList">"Favorieten"</string>
+ <string name="viewContactTitle">"Details van contactpersoon"</string>
+ <string name="viewContactDesription">"Contactpersoon weergeven"</string>
+ <string name="editContactDescription">"Contactpersoon bewerken"</string>
+ <string name="insertContactDescription">"Contactpersoon maken"</string>
+ <string name="searchHint">"Contacten zoeken"</string>
+ <string name="menu_newContact">"Nieuwe contactpersoon"</string>
+ <string name="menu_viewContact">"Contactpersoon weergeven"</string>
+ <string name="menu_callNumber">"<xliff:g id="NAME">%s</xliff:g> bellen"</string>
+ <string name="menu_addStar">"Toevoegen aan favorieten"</string>
+ <string name="menu_removeStar">"Uit favorieten verwijderen"</string>
+ <string name="menu_showBarcode">"Barcode weergeven"</string>
+ <string name="menu_editContact">"Contactpersoon bewerken"</string>
+ <string name="menu_deleteContact">"Contactpersoon verwijderen"</string>
+ <string name="menu_call">"Bellen"</string>
+ <string name="menu_sendSMS">"SMS/MMS verzenden"</string>
+ <string name="menu_sendEmail">"E-mail verzenden"</string>
+ <string name="menu_viewAddress">"Adres op kaart weergeven"</string>
+ <string name="menu_makeDefaultNumber">"Als standaardnummer instellen"</string>
+ <string name="deleteConfirmation_title">"Verwijderen"</string>
+ <string name="deleteConfirmation">"Deze contactpersoon wordt verwijderd."</string>
+ <string name="menu_done">"Gereed"</string>
+ <!-- no translation found for menu_doNotSave (2174577548513895144) -->
+ <skip />
+ <string name="editContact_title_edit">"Contactpersoon bewerken"</string>
+ <string name="editContact_title_insert">"Nieuwe contactpersoon"</string>
+ <string name="menu_addItem">"Meer informatie"</string>
+ <string name="label_phonetic_name">"Fonetisch"</string>
+ <string name="label_notes">"Opmerkingen"</string>
+ <string name="label_ringtone">"Beltoon"</string>
+ <string name="ghostData_name">"Voor- en achternaam"</string>
+ <string name="ghostData_phonetic_name">"Fonetisch gespelde naam"</string>
+ <string name="ghostData_company">"Bedrijf"</string>
+ <string name="ghostData_title">"Positie"</string>
+ <string name="ghostData_im">"Chatnaam"</string>
+ <string name="ghostData_notes">"Mijn opmerking"</string>
+ <string name="ghostData_phone">"Telefoonnummer"</string>
+ <string name="ghostData_email">"E-mailadres"</string>
+ <string name="ghostData_postal">"Postadres"</string>
+ <string name="invalidContactMessage">"De contactpersoon bestaat niet."</string>
+ <string name="pickerNewContactHeader">"Nieuwe contactpersoon maken"</string>
+ <string name="selectLabel">"Label selecteren"</string>
+ <string name="phoneLabelsGroup">"Telefoon"</string>
+ <string name="emailLabelsGroup">"E-mailadres"</string>
+ <string name="imLabelsGroup">"Chat"</string>
+ <string name="postalLabelsGroup">"Postadres"</string>
+ <string name="otherLabelsGroup">"Overig"</string>
+ <string-array name="otherLabels">
+ <item>"Organisatie"</item>
+ <item>"Opmerking"</item>
+ </string-array>
+ <string name="errorDialogTitle">"Geen foto\'s"</string>
+ <string name="photoPickerNotFoundText">"Er zijn geen foto\'s beschikbaar op de telefoon."</string>
+ <string name="attachToContact">"Pictogram voor contactpersoon"</string>
+ <string name="customLabelPickerTitle">"Aangepaste labelnaam"</string>
+ <string name="menu_displayGroup">"Groep weergeven"</string>
+ <string name="syncGroupPreference">"Synchronisatiegroepen bewerken"</string>
+ <string name="importFromSim">"Contacten importeren"</string>
+ <string name="send_to_voicemail_checkbox">"Oproepen rechtstreeks naar voicemail verzenden"</string>
+ <string name="send_to_voicemail_view">"Oproepen worden rechtstreeks naar voicemail verzonden."</string>
+ <string name="default_ringtone">"Standaard"</string>
+ <string name="addPicture">"Pictogram toevoegen"</string>
+ <string name="removePicture">"Pictogram verwijderen"</string>
+ <string name="noContacts">"Geen contacten."</string>
+ <string name="noContactsWithPhoneNumbers">"Geen contacten met telefoonnummers."</string>
+ <string name="noFavorites">"Geen favorieten."</string>
+ <string name="select_group_title">"Groepen"</string>
+ <string name="groupEmpty">"De groep \'<xliff:g id="GROUPNAME">%s</xliff:g>\' is leeg."</string>
+ <string name="showAllGroups">"Alle contacten"</string>
+ <string name="syncAllGroups">"Alle contacten synchroniseren"</string>
+ <string name="groupNameMyContacts">"Mijn contacten"</string>
+ <string name="groupNameWithPhones">"Contacten met telefoonnummers"</string>
+ <string name="contactCreatedToast">"Contactpersoon is gemaakt."</string>
+ <string name="contactSavedToast">"Contactpersoon opgeslagen."</string>
+ <string name="listSeparatorCallNumber">"Nummer bellen"</string>
+ <string name="listSeparatorCallNumber_edit">"Telefoonnummers"</string>
+ <string name="listSeparatorSendSmsMms">"SMS/MMS verzenden"</string>
+ <string name="listSeparatorSendEmail">"E-mail verzenden"</string>
+ <string name="listSeparatorSendEmail_edit">"E-mailadressen"</string>
+ <string name="listSeparatorSendIm">"Chatbericht verzenden"</string>
+ <string name="listSeparatorSendIm_edit">"Chatadressen"</string>
+ <string name="listSeparatorMapAddress">"Adres op kaart weergeven"</string>
+ <string name="listSeparatorMapAddress_edit">"Postadressen"</string>
+ <string name="listSeparatorOrganizations">"Organisaties"</string>
+ <string name="listSeparatorOtherInformation">"Overige informatie"</string>
+ <string name="listSeparatorOtherInformation_edit">"Andere opties"</string>
+ <string name="listSeparatorMore_edit">"Meer"</string>
+ <string name="contactsIconLabel">"Contacten"</string>
+ <string name="contactsFavoritesLabel">"Favorieten"</string>
+ <string name="dialerIconLabel">"Telefoon"</string>
+ <string name="recentCallsIconLabel">"Gesprekken"</string>
+ <string name="liveFolderAll">"Alle contacten"</string>
+ <string name="liveFolderFavorites">"Contacten met ster"</string>
+ <string name="liveFolderPhone">"Contacten met telefoonnummers"</string>
+ <string name="menu_sendTextMessage">"SMS-bericht verzenden"</string>
+ <string name="recentCalls_callNumber">"<xliff:g id="NAME">%s</xliff:g> bellen"</string>
+ <string name="recentCalls_editNumberBeforeCall">"Nummer bewerken voor bellen"</string>
+ <string name="recentCalls_addToContact">"Toevoegen aan contacten"</string>
+ <string name="recentCalls_removeFromRecentList">"Verwijderen uit Gesprekken"</string>
+ <string name="recentCalls_deleteAll">"Gesprekken wissen"</string>
+ <string name="recentCalls_empty">"Gesprekken is leeg"</string>
+ <string name="imei">"IMEI"</string>
+ <string name="voicemail">"Voicemail"</string>
+ <string name="unknown">"Onbekend"</string>
+ <string name="private_num">"Privénummer"</string>
+ <string name="dialerKeyboardHintText">"Toetsen gebruiken om te bellen"</string>
+ <string name="dialerDialpadHintText">"Bellen om een oproep toe te voegen"</string>
+ <string name="simContacts_emptyLoading">"Laden vanaf SIM-kaart..."</string>
+ <string name="simContacts_title">"Contacten op SIM-kaart"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"Synchroniseer uw Google-contacten!"</font>" "\n"Zodra uw telefoon is gesynchroniseerd, heeft u uw contacten altijd ter beschikking."</string>
+ <!-- no translation found for noContactsHelpText (2249195687463896364) -->
+ <skip />
+ <!-- no translation found for noContactsHelpTextWithSync (4927701496550314555) -->
+ <skip />
+ <string name="seclectSyncGroups_title">"Groepen selecteren om te synchroniseren"</string>
+ <string name="liveFolder_all_label">"Alle contacten"</string>
+ <string name="liveFolder_favorites_label">"Met ster"</string>
+ <string name="liveFolder_phones_label">"Telefoons"</string>
+ <string name="dialer_useDtmfDialpad">"Toetsen voor toonkiezen gebruiken"</string>
+ <string name="dialer_returnToInCallScreen">"Terug naar actief gesprek"</string>
+ <string name="dialer_addAnotherCall">"Oproep toevoegen"</string>
+ <string name="callDetailTitle">"Gespreksgegevens"</string>
+ <string name="toast_call_detail_error">"Kan geen gegevens over het gevraagde gesprek vinden."</string>
+ <string name="call_type">"Type"</string>
+ <string name="type_incoming">"Binnenkomende oproep"</string>
+ <string name="type_outgoing">"Uitgaand gesprek"</string>
+ <string name="type_missed">"Oproep gemist"</string>
+ <string name="call_time">"Tijd"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g> (<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"Duur"</string>
+ <string name="header_actions">"Acties"</string>
+ <string name="ringtone_spinner">"Beltoon: <xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"Meer items toevoegen"</string>
+ <string name="actionCall">"<xliff:g id="TYPE">%s</xliff:g> bellen"</string>
+ <string name="actionEmail">"E-mail <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionText">"Tekst <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionChat">"Chatten via <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionMap">"<xliff:g id="TYPE">%s</xliff:g>-adres bekijken"</string>
+ <string name="actionIncomingCall">"Binnenkomende oproepen"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"Wordt direct naar voicemail verzonden"</string>
+ <string name="detailsRingtone">"Ingesteld op <xliff:g id="RINGTONE_NAME">%s</xliff:g>"</string>
+ <string name="callBack">"Terugbellen"</string>
+ <!-- no translation found for callAgain (3197312117049874778) -->
+ <skip />
+ <!-- no translation found for returnCall (8171961914203617813) -->
+ <skip />
+ <!-- no translation found for callDetailsDurationFormat (8157706382818184268) -->
+ <skip />
+</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
new file mode 100644
index 000000000..feb5c8bfb
--- /dev/null
+++ b/res/values-pl/strings.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"Kontakty"</string>
+ <!-- no translation found for launcherDialer (140610573639849799) -->
+ <skip />
+ <string name="shortcutContact">"Kontakt"</string>
+ <string name="starredList">"Oznaczony gwiazdką"</string>
+ <string name="frequentList">"Częste"</string>
+ <string name="strequentList">"Ulubione"</string>
+ <string name="viewContactTitle">"Szczegóły kontaktu"</string>
+ <string name="viewContactDesription">"Wyświetl kontakt"</string>
+ <string name="editContactDescription">"Edytuj kontakt"</string>
+ <string name="insertContactDescription">"Utwórz kontakt"</string>
+ <string name="searchHint">"Przeszukuj kontakty"</string>
+ <string name="menu_newContact">"Nowy kontakt"</string>
+ <string name="menu_viewContact">"Wyświetl kontakt"</string>
+ <string name="menu_callNumber">"Zadzwoń do kontaktu <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="menu_addStar">"Dodaj do ulubionych"</string>
+ <string name="menu_removeStar">"Usuń z ulubionych"</string>
+ <string name="menu_showBarcode">"Pokaż kod kreskowy"</string>
+ <string name="menu_editContact">"Edytuj kontakt"</string>
+ <string name="menu_deleteContact">"Usuń kontakt"</string>
+ <string name="menu_call">"Zadzwoń"</string>
+ <string name="menu_sendSMS">"Wyślij wiadomość SMS/MMS"</string>
+ <string name="menu_sendEmail">"Wyślij wiadomość e-mail"</string>
+ <string name="menu_viewAddress">"Pokaż adres na mapie"</string>
+ <string name="menu_makeDefaultNumber">"Ustaw ten numer jako domyślny"</string>
+ <string name="deleteConfirmation_title">"Usuń"</string>
+ <string name="deleteConfirmation">"Ten kontakt zostanie usunięty."</string>
+ <string name="menu_done">"Gotowe"</string>
+ <!-- no translation found for menu_doNotSave (2174577548513895144) -->
+ <skip />
+ <string name="editContact_title_edit">"Edytuj kontakt"</string>
+ <string name="editContact_title_insert">"Nowy kontakt"</string>
+ <string name="menu_addItem">"Więcej informacji"</string>
+ <string name="label_phonetic_name">"Fonetycznie"</string>
+ <string name="label_notes">"Notatki"</string>
+ <string name="label_ringtone">"Dzwonek"</string>
+ <string name="ghostData_name">"Imię i nazwisko"</string>
+ <string name="ghostData_phonetic_name">"Nazwisko (fonetycznie)"</string>
+ <string name="ghostData_company">"Firma"</string>
+ <string name="ghostData_title">"Pozycja"</string>
+ <string name="ghostData_im">"Nazwa w komunikatorze"</string>
+ <string name="ghostData_notes">"Moja notatka"</string>
+ <string name="ghostData_phone">"Numer telefonu"</string>
+ <string name="ghostData_email">"Adres e-mail"</string>
+ <string name="ghostData_postal">"Adres pocztowy"</string>
+ <string name="invalidContactMessage">"Kontakt nie istnieje."</string>
+ <string name="pickerNewContactHeader">"Utwórz nowy kontakt"</string>
+ <string name="selectLabel">"Wybierz etykietę"</string>
+ <string name="phoneLabelsGroup">"Telefon"</string>
+ <string name="emailLabelsGroup">"E-mail"</string>
+ <string name="imLabelsGroup">"Komunikatory"</string>
+ <string name="postalLabelsGroup">"Adres pocztowy"</string>
+ <string name="otherLabelsGroup">"Inne"</string>
+ <string-array name="otherLabels">
+ <item>"Organizacja"</item>
+ <item>"Notatka"</item>
+ </string-array>
+ <string name="errorDialogTitle">"Brak zdjęć"</string>
+ <string name="photoPickerNotFoundText">"W telefonie brak dostępnych zdjęć."</string>
+ <string name="attachToContact">"Ikona kontaktu"</string>
+ <string name="customLabelPickerTitle">"Nazwa etykiety niestandardowej"</string>
+ <string name="menu_displayGroup">"Wyświetl grupę"</string>
+ <string name="syncGroupPreference">"Synchronizacja grup"</string>
+ <string name="importFromSim">"Importuj kontakty"</string>
+ <string name="send_to_voicemail_checkbox">"Przekieruj połączenia bezpośrednio na pocztę głosową"</string>
+ <string name="send_to_voicemail_view">"Połączenia są przekazywane bezpośrednio do poczty głosowej."</string>
+ <string name="default_ringtone">"Domyślny"</string>
+ <string name="addPicture">"Dodaj ikonę"</string>
+ <string name="removePicture">"Usuń ikonę"</string>
+ <string name="noContacts">"Brak kontaktów."</string>
+ <string name="noContactsWithPhoneNumbers">"Brak kontaktów z numerami telefonów."</string>
+ <string name="noFavorites">"Brak ulubionych"</string>
+ <string name="select_group_title">"Grupy"</string>
+ <string name="groupEmpty">"Grupa „<xliff:g id="GROUPNAME">%s</xliff:g>” jest pusta."</string>
+ <string name="showAllGroups">"Wszystkie kontakty"</string>
+ <string name="syncAllGroups">"Synchronizuj wszystkie kontakty"</string>
+ <string name="groupNameMyContacts">"Moje kontakty"</string>
+ <string name="groupNameWithPhones">"Kontakty z numerami telefonu"</string>
+ <string name="contactCreatedToast">"Utworzono kontakt."</string>
+ <string name="contactSavedToast">"Kontakt został zapisany."</string>
+ <string name="listSeparatorCallNumber">"Wybierz numer"</string>
+ <string name="listSeparatorCallNumber_edit">"Numery telefonów"</string>
+ <string name="listSeparatorSendSmsMms">"Wyślij wiadomość SMS/MMS"</string>
+ <string name="listSeparatorSendEmail">"Wyślij wiadomość e-mail"</string>
+ <string name="listSeparatorSendEmail_edit">"Adresy e-mail"</string>
+ <string name="listSeparatorSendIm">"Wyślij wiadomość błyskawiczną"</string>
+ <string name="listSeparatorSendIm_edit">"Adresy czatu"</string>
+ <string name="listSeparatorMapAddress">"Pokaż adres na mapie"</string>
+ <string name="listSeparatorMapAddress_edit">"Adresy pocztowe"</string>
+ <string name="listSeparatorOrganizations">"Organizacje"</string>
+ <string name="listSeparatorOtherInformation">"Inne informacje"</string>
+ <string name="listSeparatorOtherInformation_edit">"Inne opcje"</string>
+ <string name="listSeparatorMore_edit">"Więcej"</string>
+ <string name="contactsIconLabel">"Kontakty"</string>
+ <string name="contactsFavoritesLabel">"Ulubione"</string>
+ <string name="dialerIconLabel">"Telefon"</string>
+ <string name="recentCallsIconLabel">"Rejestr"</string>
+ <string name="liveFolderAll">"Wszystkie kontakty"</string>
+ <string name="liveFolderFavorites">"Kontakty oznaczone gwiazdką"</string>
+ <string name="liveFolderPhone">"Kontakty z numerami telefonów"</string>
+ <string name="menu_sendTextMessage">"Wyślij wiadomość SMS"</string>
+ <string name="recentCalls_callNumber">"Zadzwoń do kontaktu <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="recentCalls_editNumberBeforeCall">"Edytuj numer przed nawiązaniem połączenia"</string>
+ <string name="recentCalls_addToContact">"Dodaj do kontaktów"</string>
+ <string name="recentCalls_removeFromRecentList">"Usuń z rejestru połączeń"</string>
+ <string name="recentCalls_deleteAll">"Wyczyść rejestr połączeń"</string>
+ <string name="recentCalls_empty">"Rejestr połączeń jest pusty."</string>
+ <string name="imei">"IMEI"</string>
+ <string name="voicemail">"Poczta głosowa"</string>
+ <string name="unknown">"Nieznane"</string>
+ <string name="private_num">"Numer prywatny"</string>
+ <string name="dialerKeyboardHintText">"Wybierz numer za pomocą klawiatury"</string>
+ <string name="dialerDialpadHintText">"Wybierz numer, aby dodać połączenie"</string>
+ <string name="simContacts_emptyLoading">"Wczytywanie z karty SIM…"</string>
+ <string name="simContacts_title">"Kontakty z karty SIM"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"Zsynchronizuj kontakty Google!"</font>" "\n"Po zsynchronizowaniu telefonu zawsze będziesz mieć kontakty pod ręką."</string>
+ <!-- no translation found for noContactsHelpText (2249195687463896364) -->
+ <skip />
+ <!-- no translation found for noContactsHelpTextWithSync (4927701496550314555) -->
+ <skip />
+ <string name="seclectSyncGroups_title">"Wybierz grupy do zsynchronizowania"</string>
+ <string name="liveFolder_all_label">"Wszystkie kontakty"</string>
+ <string name="liveFolder_favorites_label">"Oznaczone gwiazdką"</string>
+ <string name="liveFolder_phones_label">"Telefony"</string>
+ <string name="dialer_useDtmfDialpad">"Użyj klawiatury tonowej"</string>
+ <string name="dialer_returnToInCallScreen">"Powrót do aktywnego połączenia"</string>
+ <string name="dialer_addAnotherCall">"Dodaj połączenie"</string>
+ <string name="callDetailTitle">"Szczegóły połączenia"</string>
+ <string name="toast_call_detail_error">"Nie można odczytać szczegółów żądanego połączenia."</string>
+ <string name="call_type">"Typ"</string>
+ <string name="type_incoming">"Poł. przychodzące"</string>
+ <string name="type_outgoing">"Połączenie wychodzące"</string>
+ <string name="type_missed">"Nieodebrane połączenia"</string>
+ <string name="call_time">"Godzina"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g> (<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"Czas trwania"</string>
+ <string name="header_actions">"Czynności"</string>
+ <string name="ringtone_spinner">"Dźwięk dzwonka: <xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"Dodaj więcej elementów"</string>
+ <string name="actionCall">"Zadzwoń na numer: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionEmail">"Wyślij wiadomość e-mail na adres: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionText">"Wyślij tekst do: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionChat">"Porozmawiaj na czacie <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionMap">"Wyświetl adres: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionIncomingCall">"Połączenia przychodzące"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"Zostaną wysłane bezpośrednio na pocztę głosową"</string>
+ <string name="detailsRingtone">"Ustaw na <xliff:g id="RINGTONE_NAME">%s</xliff:g>"</string>
+ <string name="callBack">"Oddzwoń"</string>
+ <!-- no translation found for callAgain (3197312117049874778) -->
+ <skip />
+ <!-- no translation found for returnCall (8171961914203617813) -->
+ <skip />
+ <!-- no translation found for callDetailsDurationFormat (8157706382818184268) -->
+ <skip />
+</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
new file mode 100644
index 000000000..398c89835
--- /dev/null
+++ b/res/values-ru/strings.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"Контакты"</string>
+ <!-- no translation found for launcherDialer (140610573639849799) -->
+ <skip />
+ <string name="shortcutContact">"Контакт"</string>
+ <string name="starredList">"Помеченные"</string>
+ <string name="frequentList">"Частые"</string>
+ <string name="strequentList">"Избранные"</string>
+ <string name="viewContactTitle">"Сведения о контакте"</string>
+ <string name="viewContactDesription">"Просмотреть контакт"</string>
+ <string name="editContactDescription">"Изменить контакт"</string>
+ <string name="insertContactDescription">"Создать контакт"</string>
+ <string name="searchHint">"Искать контакты"</string>
+ <string name="menu_newContact">"Создать контакт"</string>
+ <string name="menu_viewContact">"Просмотреть контакт"</string>
+ <string name="menu_callNumber">"Позвонить: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="menu_addStar">"Добавить в избранное"</string>
+ <string name="menu_removeStar">"Удалить из избранного"</string>
+ <string name="menu_showBarcode">"Показать штрихкод"</string>
+ <string name="menu_editContact">"Изменить контакт"</string>
+ <string name="menu_deleteContact">"Удалить контакт"</string>
+ <string name="menu_call">"Позвонить"</string>
+ <string name="menu_sendSMS">"Отправить SMS/MMS"</string>
+ <string name="menu_sendEmail">"Отправить сообщение электронной почты"</string>
+ <string name="menu_viewAddress">"Адрес на карте"</string>
+ <string name="menu_makeDefaultNumber">"Сделать номером по умолчанию"</string>
+ <string name="deleteConfirmation_title">"Удаление"</string>
+ <string name="deleteConfirmation">"Этот контакт будет удален."</string>
+ <string name="menu_done">"Готово"</string>
+ <!-- no translation found for menu_doNotSave (2174577548513895144) -->
+ <skip />
+ <string name="editContact_title_edit">"Изменить контакт"</string>
+ <string name="editContact_title_insert">"Создание контакта"</string>
+ <string name="menu_addItem">"Подробнее"</string>
+ <string name="label_phonetic_name">"Фонетика"</string>
+ <string name="label_notes">"Примечания"</string>
+ <string name="label_ringtone">"Мелодия звонка"</string>
+ <string name="ghostData_name">"Первый и последний"</string>
+ <string name="ghostData_phonetic_name">"Имя (фонетическая запись)"</string>
+ <string name="ghostData_company">"Компания"</string>
+ <string name="ghostData_title">"Должность"</string>
+ <string name="ghostData_im">"Имя в чате"</string>
+ <string name="ghostData_notes">"Мое примечание"</string>
+ <string name="ghostData_phone">"Телефонный номер"</string>
+ <string name="ghostData_email">"Адрес электронной почты"</string>
+ <string name="ghostData_postal">"Почтовый адрес"</string>
+ <string name="invalidContactMessage">"Контакт не существует."</string>
+ <string name="pickerNewContactHeader">"Создать новый контакт"</string>
+ <string name="selectLabel">"Выбор ярлыка"</string>
+ <string name="phoneLabelsGroup">"Телефон"</string>
+ <string name="emailLabelsGroup">"Электронная почта"</string>
+ <string name="imLabelsGroup">"Чат"</string>
+ <string name="postalLabelsGroup">"Почтовый адрес"</string>
+ <string name="otherLabelsGroup">"Прочее"</string>
+ <string-array name="otherLabels">
+ <item>"Организация"</item>
+ <item>"Примечание"</item>
+ </string-array>
+ <string name="errorDialogTitle">"Нет изображений"</string>
+ <string name="photoPickerNotFoundText">"На телефоне нет изображений."</string>
+ <string name="attachToContact">"Значок контакта"</string>
+ <string name="customLabelPickerTitle">"Имя персонализированного ярлыка"</string>
+ <string name="menu_displayGroup">"Показать группу"</string>
+ <string name="syncGroupPreference">"Изменить группы синхронизации"</string>
+ <string name="importFromSim">"Импортировать контакты"</string>
+ <string name="send_to_voicemail_checkbox">"Направлять звонки в голосовую почту"</string>
+ <string name="send_to_voicemail_view">"Вызовы направляются прямо в голосовую почту."</string>
+ <string name="default_ringtone">"По умолчанию"</string>
+ <string name="addPicture">"Добавить значок"</string>
+ <string name="removePicture">"Убрать значок"</string>
+ <string name="noContacts">"Нет контактов."</string>
+ <string name="noContactsWithPhoneNumbers">"Нет контактов с телефонными номерами."</string>
+ <string name="noFavorites">"Нет избранных."</string>
+ <string name="select_group_title">"Группы"</string>
+ <string name="groupEmpty">"Группа \"<xliff:g id="GROUPNAME">%s</xliff:g>\" пуста."</string>
+ <string name="showAllGroups">"Все контакты"</string>
+ <string name="syncAllGroups">"Синхронизировать все контакты"</string>
+ <string name="groupNameMyContacts">"Мои контакты"</string>
+ <string name="groupNameWithPhones">"Контакты с телефонными номерами"</string>
+ <string name="contactCreatedToast">"Контакт создан."</string>
+ <string name="contactSavedToast">"Контакт сохранен."</string>
+ <string name="listSeparatorCallNumber">"Набрать номер"</string>
+ <string name="listSeparatorCallNumber_edit">"Номера телефонов"</string>
+ <string name="listSeparatorSendSmsMms">"Отправить SMS/MMS"</string>
+ <string name="listSeparatorSendEmail">"Отправить сообщение электронной почты"</string>
+ <string name="listSeparatorSendEmail_edit">"Адреса электронной почты"</string>
+ <string name="listSeparatorSendIm">"Отправить мгновенное сообщение"</string>
+ <string name="listSeparatorSendIm_edit">"Адреса чата"</string>
+ <string name="listSeparatorMapAddress">"Адрес на карте"</string>
+ <string name="listSeparatorMapAddress_edit">"Почтовые адреса"</string>
+ <string name="listSeparatorOrganizations">"Организации"</string>
+ <string name="listSeparatorOtherInformation">"Другие сведения"</string>
+ <string name="listSeparatorOtherInformation_edit">"Другие параметры"</string>
+ <string name="listSeparatorMore_edit">"Дополнительно"</string>
+ <string name="contactsIconLabel">"Контакты"</string>
+ <string name="contactsFavoritesLabel">"Избранные"</string>
+ <string name="dialerIconLabel">"Набор номеров"</string>
+ <string name="recentCallsIconLabel">"Журнал вызовов"</string>
+ <string name="liveFolderAll">"Все контакты"</string>
+ <string name="liveFolderFavorites">"Помеченные контакты"</string>
+ <string name="liveFolderPhone">"Контакты с номерами телефонов"</string>
+ <string name="menu_sendTextMessage">"Отправить сообщение SMS"</string>
+ <string name="recentCalls_callNumber">"Позвонить: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="recentCalls_editNumberBeforeCall">"Изменить номер перед вызовом"</string>
+ <string name="recentCalls_addToContact">"Добавить в контакты"</string>
+ <string name="recentCalls_removeFromRecentList">"Удалить из журнала вызовов"</string>
+ <string name="recentCalls_deleteAll">"Очистить журнал вызовов"</string>
+ <string name="recentCalls_empty">"Журнал вызовов пуст."</string>
+ <string name="imei">"IMEI"</string>
+ <string name="voicemail">"Голосовая почта"</string>
+ <string name="unknown">"Неизвестно"</string>
+ <string name="private_num">"Частный номер"</string>
+ <string name="dialerKeyboardHintText">"Для набора воспользуйтесь клавиатурой"</string>
+ <string name="dialerDialpadHintText">"Наберите номер, чтобы добавить вызов"</string>
+ <string name="simContacts_emptyLoading">"Идет загрузка с SIM-карты…"</string>
+ <string name="simContacts_title">"Контакты на SIM-карте"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"Синхронизируйте свои контакты Google!"</font>" "\n"После синхронизации с телефоном вы сможете общаться со знакомыми, где бы вы ни были."</string>
+ <!-- no translation found for noContactsHelpText (2249195687463896364) -->
+ <skip />
+ <!-- no translation found for noContactsHelpTextWithSync (4927701496550314555) -->
+ <skip />
+ <string name="seclectSyncGroups_title">"Выбор групп для синхронизации"</string>
+ <string name="liveFolder_all_label">"Все контакты"</string>
+ <string name="liveFolder_favorites_label">"Помеченные"</string>
+ <string name="liveFolder_phones_label">"Телефоны"</string>
+ <string name="dialer_useDtmfDialpad">"Использовать клавиатуру тонального набора"</string>
+ <string name="dialer_returnToInCallScreen">"Вернуться к текущему звонку"</string>
+ <string name="dialer_addAnotherCall">"Добавить звонок"</string>
+ <string name="callDetailTitle">"Сведения о звонке"</string>
+ <string name="toast_call_detail_error">"Не удается считать сведения об указанном звонке."</string>
+ <string name="call_type">"Тип"</string>
+ <string name="type_incoming">"Входящий звонок"</string>
+ <string name="type_outgoing">"Исходящий звонок"</string>
+ <string name="type_missed">"Пропущенный звонок"</string>
+ <string name="call_time">"Время"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g> (<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"Продолжительность"</string>
+ <string name="header_actions">"Действия"</string>
+ <string name="ringtone_spinner">"Мелодия: <xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"Добавление контактов"</string>
+ <string name="actionCall">"Позвонить по номеру: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionEmail">"Отправить электронное сообщение на адрес: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionText">"Отправить текстовое сообщение на номер: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionChat">"Общаться в чате с помощью: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionMap">"Просмотр адреса: <xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionIncomingCall">"Входящие вызовы"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"Переадресация в голосовую почту"</string>
+ <string name="detailsRingtone">"Выбрано: <xliff:g id="RINGTONE_NAME">%s</xliff:g>"</string>
+ <string name="callBack">"Перезвонить"</string>
+ <!-- no translation found for callAgain (3197312117049874778) -->
+ <skip />
+ <!-- no translation found for returnCall (8171961914203617813) -->
+ <skip />
+ <!-- no translation found for callDetailsDurationFormat (8157706382818184268) -->
+ <skip />
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000..6ef964e9e
--- /dev/null
+++ b/res/values-zh-rCN/strings.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"联系人"</string>
+ <!-- no translation found for launcherDialer (140610573639849799) -->
+ <skip />
+ <string name="shortcutContact">"联系人"</string>
+ <string name="starredList">"已加星标"</string>
+ <string name="frequentList">"经常"</string>
+ <string name="strequentList">"收藏夹"</string>
+ <string name="viewContactTitle">"联系人详细信息"</string>
+ <string name="viewContactDesription">"查看联系人"</string>
+ <string name="editContactDescription">"编辑联系人"</string>
+ <string name="insertContactDescription">"创建联系人"</string>
+ <string name="searchHint">"搜索联系人"</string>
+ <string name="menu_newContact">"添加联系人"</string>
+ <string name="menu_viewContact">"查看联系人"</string>
+ <string name="menu_callNumber">"呼叫<xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="menu_addStar">"添加到收藏夹"</string>
+ <string name="menu_removeStar">"从收藏夹中删除"</string>
+ <string name="menu_showBarcode">"显示条形码"</string>
+ <string name="menu_editContact">"编辑联系人"</string>
+ <string name="menu_deleteContact">"删除联系人"</string>
+ <string name="menu_call">"呼叫"</string>
+ <string name="menu_sendSMS">"发送短信/彩信"</string>
+ <string name="menu_sendEmail">"发送电子邮件"</string>
+ <string name="menu_viewAddress">"地图地址"</string>
+ <string name="menu_makeDefaultNumber">"设置默认号码"</string>
+ <string name="deleteConfirmation_title">"删除"</string>
+ <string name="deleteConfirmation">"会删除此联系人。"</string>
+ <string name="menu_done">"完成"</string>
+ <!-- no translation found for menu_doNotSave (2174577548513895144) -->
+ <skip />
+ <string name="editContact_title_edit">"编辑联系人"</string>
+ <string name="editContact_title_insert">"添加联系人"</string>
+ <string name="menu_addItem">"详细信息"</string>
+ <string name="label_phonetic_name">"拼音"</string>
+ <string name="label_notes">"备注"</string>
+ <string name="label_ringtone">"铃声"</string>
+ <string name="ghostData_name">"第一个和最后一个"</string>
+ <string name="ghostData_phonetic_name">"拼音姓名"</string>
+ <string name="ghostData_company">"公司"</string>
+ <string name="ghostData_title">"位置"</string>
+ <string name="ghostData_im">"即时消息名称"</string>
+ <string name="ghostData_notes">"我的备注"</string>
+ <string name="ghostData_phone">"电话号码"</string>
+ <string name="ghostData_email">"电子邮件地址"</string>
+ <string name="ghostData_postal">"通讯地址"</string>
+ <string name="invalidContactMessage">"该联系人不存在。"</string>
+ <string name="pickerNewContactHeader">"创建新联系人"</string>
+ <string name="selectLabel">"选择标签"</string>
+ <string name="phoneLabelsGroup">"手机"</string>
+ <string name="emailLabelsGroup">"电子邮件"</string>
+ <string name="imLabelsGroup">"即时消息"</string>
+ <string name="postalLabelsGroup">"通讯地址"</string>
+ <string name="otherLabelsGroup">"其他"</string>
+ <string-array name="otherLabels">
+ <item>"组织"</item>
+ <item>"备注"</item>
+ </string-array>
+ <string name="errorDialogTitle">"没有图片"</string>
+ <string name="photoPickerNotFoundText">"手机上没有可用的图片。"</string>
+ <string name="attachToContact">"“联系人”图标"</string>
+ <string name="customLabelPickerTitle">"自定义标签名称"</string>
+ <string name="menu_displayGroup">"显示组"</string>
+ <string name="syncGroupPreference">"编辑同步组"</string>
+ <string name="importFromSim">"导入联系人"</string>
+ <string name="send_to_voicemail_checkbox">"将通话直接发送到语音邮件"</string>
+ <string name="send_to_voicemail_view">"呼叫被直接发送至语音邮件。"</string>
+ <string name="default_ringtone">"默认"</string>
+ <string name="addPicture">"“添加”图标"</string>
+ <string name="removePicture">"“删除”图标"</string>
+ <string name="noContacts">"没有联系人。"</string>
+ <string name="noContactsWithPhoneNumbers">"没有联系人拥有电话号码。"</string>
+ <string name="noFavorites">"没有收藏内容。"</string>
+ <string name="select_group_title">"组"</string>
+ <string name="groupEmpty">"您的“<xliff:g id="GROUPNAME">%s</xliff:g>”组为空。"</string>
+ <string name="showAllGroups">"所有联系人"</string>
+ <string name="syncAllGroups">"同步所有联系人"</string>
+ <string name="groupNameMyContacts">"我的联系人"</string>
+ <string name="groupNameWithPhones">"拥有电话号码的联系人"</string>
+ <string name="contactCreatedToast">"已创建“联系人”。"</string>
+ <string name="contactSavedToast">"已保存联系人。"</string>
+ <string name="listSeparatorCallNumber">"拔号"</string>
+ <string name="listSeparatorCallNumber_edit">"电话号码"</string>
+ <string name="listSeparatorSendSmsMms">"发送短信/彩信"</string>
+ <string name="listSeparatorSendEmail">"发送电子邮件"</string>
+ <string name="listSeparatorSendEmail_edit">"电子邮件地址"</string>
+ <string name="listSeparatorSendIm">"发送即时消息"</string>
+ <string name="listSeparatorSendIm_edit">"聊天地址"</string>
+ <string name="listSeparatorMapAddress">"地图地址"</string>
+ <string name="listSeparatorMapAddress_edit">"邮政地址"</string>
+ <string name="listSeparatorOrganizations">"组织"</string>
+ <string name="listSeparatorOtherInformation">"其他信息"</string>
+ <string name="listSeparatorOtherInformation_edit">"其他选项"</string>
+ <string name="listSeparatorMore_edit">"更多"</string>
+ <string name="contactsIconLabel">"联系人"</string>
+ <string name="contactsFavoritesLabel">"收藏夹"</string>
+ <string name="dialerIconLabel">"拔号程序"</string>
+ <string name="recentCallsIconLabel">"通话记录"</string>
+ <string name="liveFolderAll">"所有联系人"</string>
+ <string name="liveFolderFavorites">"已加星标的联系人"</string>
+ <string name="liveFolderPhone">"有电话号码的联系人"</string>
+ <string name="menu_sendTextMessage">"发送短信"</string>
+ <string name="recentCalls_callNumber">"呼叫<xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="recentCalls_editNumberBeforeCall">"在呼叫之前编辑号码"</string>
+ <string name="recentCalls_addToContact">"添加到联系人"</string>
+ <string name="recentCalls_removeFromRecentList">"从通话记录中删除"</string>
+ <string name="recentCalls_deleteAll">"清除通话记录"</string>
+ <string name="recentCalls_empty">"通话记录为空。"</string>
+ <string name="imei">"移动通信国际识别码"</string>
+ <string name="voicemail">"语音邮件"</string>
+ <string name="unknown">"未知"</string>
+ <string name="private_num">"私人号码"</string>
+ <string name="dialerKeyboardHintText">"使用键盘拔号"</string>
+ <string name="dialerDialpadHintText">"通过拨号添加通话"</string>
+ <string name="simContacts_emptyLoading">"正在从 SIM 卡中载入..."</string>
+ <string name="simContacts_title">"SIM 卡联系人"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">"同步您的 Google 联系人!"</font>\n"将联系人同步到您的手机后,无论您走到哪里,都可以与他们联系。"</string>
+ <!-- no translation found for noContactsHelpText (2249195687463896364) -->
+ <skip />
+ <!-- no translation found for noContactsHelpTextWithSync (4927701496550314555) -->
+ <skip />
+ <string name="seclectSyncGroups_title">"选择要同步的组"</string>
+ <string name="liveFolder_all_label">"所有联系人"</string>
+ <string name="liveFolder_favorites_label">"已加星标"</string>
+ <string name="liveFolder_phones_label">"电话"</string>
+ <string name="dialer_useDtmfDialpad">"使用按键式键盘"</string>
+ <string name="dialer_returnToInCallScreen">"返回到正在进行中的通话"</string>
+ <string name="dialer_addAnotherCall">"添加通话"</string>
+ <string name="callDetailTitle">"通话详情"</string>
+ <string name="toast_call_detail_error">"无法阅读所请求通话的详情。"</string>
+ <string name="call_type">"类型"</string>
+ <string name="type_incoming">"来电"</string>
+ <string name="type_outgoing">"去电"</string>
+ <string name="type_missed">"未接来电"</string>
+ <string name="call_time">"时间"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g>(<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"持续时间"</string>
+ <string name="header_actions">"操作"</string>
+ <string name="ringtone_spinner">"铃声:<xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"添加更多项目"</string>
+ <string name="actionCall">"呼叫<xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionEmail">"向<xliff:g id="TYPE">%s</xliff:g>发送电子邮件"</string>
+ <string name="actionText">"向<xliff:g id="TYPE">%s</xliff:g>发送文本消息"</string>
+ <string name="actionChat">"使用 <xliff:g id="TYPE">%s</xliff:g> 进行聊天"</string>
+ <string name="actionMap">"查看<xliff:g id="TYPE">%s</xliff:g>地址"</string>
+ <string name="actionIncomingCall">"来电"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"将直接发送至语音邮件"</string>
+ <string name="detailsRingtone">"设置为“<xliff:g id="RINGTONE_NAME">%s</xliff:g>”"</string>
+ <string name="callBack">"回拨"</string>
+ <!-- no translation found for callAgain (3197312117049874778) -->
+ <skip />
+ <!-- no translation found for returnCall (8171961914203617813) -->
+ <skip />
+ <!-- no translation found for callDetailsDurationFormat (8157706382818184268) -->
+ <skip />
+</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000..91704fb62
--- /dev/null
+++ b/res/values-zh-rTW/strings.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="contactsList">"連絡人"</string>
+ <!-- no translation found for launcherDialer (140610573639849799) -->
+ <skip />
+ <string name="shortcutContact">"連絡人"</string>
+ <string name="starredList">"已標記星號"</string>
+ <string name="frequentList">"經常聯絡"</string>
+ <string name="strequentList">"我的最愛"</string>
+ <string name="viewContactTitle">"連絡人詳細資料"</string>
+ <string name="viewContactDesription">"檢視連絡人"</string>
+ <string name="editContactDescription">"編輯連絡人"</string>
+ <string name="insertContactDescription">"建立連絡人"</string>
+ <string name="searchHint">"搜尋連絡人"</string>
+ <string name="menu_newContact">"新增連絡人"</string>
+ <string name="menu_viewContact">"檢視連絡人"</string>
+ <string name="menu_callNumber">"撥打電話給 <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="menu_addStar">"新增至我的最愛"</string>
+ <string name="menu_removeStar">"從我的最愛中移除"</string>
+ <string name="menu_showBarcode">"顯示條碼"</string>
+ <string name="menu_editContact">"編輯連絡人"</string>
+ <string name="menu_deleteContact">"刪除連絡人"</string>
+ <string name="menu_call">"通話"</string>
+ <string name="menu_sendSMS">"傳送 SMS/MMS"</string>
+ <string name="menu_sendEmail">"傳送電子郵件"</string>
+ <string name="menu_viewAddress">"在地圖上顯示地址"</string>
+ <string name="menu_makeDefaultNumber">"設為預設號碼"</string>
+ <string name="deleteConfirmation_title">"刪除"</string>
+ <string name="deleteConfirmation">"此連絡人會被刪除。"</string>
+ <string name="menu_done">"完成"</string>
+ <!-- no translation found for menu_doNotSave (2174577548513895144) -->
+ <skip />
+ <string name="editContact_title_edit">"編輯連絡人"</string>
+ <string name="editContact_title_insert">"新增連絡人"</string>
+ <string name="menu_addItem">"更多資訊"</string>
+ <string name="label_phonetic_name">"拼音"</string>
+ <string name="label_notes">"附註"</string>
+ <string name="label_ringtone">"鈴聲"</string>
+ <string name="ghostData_name">"姓名"</string>
+ <string name="ghostData_phonetic_name">"姓名拼音"</string>
+ <string name="ghostData_company">"公司"</string>
+ <string name="ghostData_title">"職位"</string>
+ <string name="ghostData_im">"即時訊息帳號"</string>
+ <string name="ghostData_notes">"我的附註"</string>
+ <string name="ghostData_phone">"電話號碼"</string>
+ <string name="ghostData_email">"電子郵件地址"</string>
+ <string name="ghostData_postal">"郵寄地址"</string>
+ <string name="invalidContactMessage">"連絡人不存在"</string>
+ <string name="pickerNewContactHeader">"建立新連絡人"</string>
+ <string name="selectLabel">"選取標籤"</string>
+ <string name="phoneLabelsGroup">"電話"</string>
+ <string name="emailLabelsGroup">"電子郵件"</string>
+ <string name="imLabelsGroup">"即時訊息"</string>
+ <string name="postalLabelsGroup">"郵寄地址"</string>
+ <string name="otherLabelsGroup">"其他"</string>
+ <string-array name="otherLabels">
+ <item>"組織"</item>
+ <item>"附註"</item>
+ </string-array>
+ <string name="errorDialogTitle">"沒有相片"</string>
+ <string name="photoPickerNotFoundText">"手機上沒有相片 。"</string>
+ <string name="attachToContact">"連絡人圖示"</string>
+ <string name="customLabelPickerTitle">"自訂標籤名稱"</string>
+ <string name="menu_displayGroup">"顯示群組"</string>
+ <string name="syncGroupPreference">"編輯同步處理群組"</string>
+ <string name="importFromSim">"匯入連絡人"</string>
+ <string name="send_to_voicemail_checkbox">"直接將通話轉到語音郵件"</string>
+ <string name="send_to_voicemail_view">"通話已直接轉至語音信箱"</string>
+ <string name="default_ringtone">"預設值"</string>
+ <string name="addPicture">"新增圖示"</string>
+ <string name="removePicture">"移除圖示"</string>
+ <string name="noContacts">"沒有連絡人"</string>
+ <string name="noContactsWithPhoneNumbers">"沒有連絡人有電話號碼。"</string>
+ <string name="noFavorites">"沒有我的最愛。"</string>
+ <string name="select_group_title">"群組"</string>
+ <string name="groupEmpty">"您的「<xliff:g id="GROUPNAME">%s</xliff:g>」群組是空的。"</string>
+ <string name="showAllGroups">"所有連絡人"</string>
+ <string name="syncAllGroups">"同步處理所有連絡人"</string>
+ <string name="groupNameMyContacts">"我的聯絡人"</string>
+ <string name="groupNameWithPhones">"有電話號碼的連絡人"</string>
+ <string name="contactCreatedToast">"已建立連絡人"</string>
+ <string name="contactSavedToast">"連絡人已儲存。"</string>
+ <string name="listSeparatorCallNumber">"撥打號碼"</string>
+ <string name="listSeparatorCallNumber_edit">"電話號碼"</string>
+ <string name="listSeparatorSendSmsMms">"傳送 SMS/MMS"</string>
+ <string name="listSeparatorSendEmail">"傳送電子郵件"</string>
+ <string name="listSeparatorSendEmail_edit">"電子郵件地址"</string>
+ <string name="listSeparatorSendIm">"傳送即時訊息"</string>
+ <string name="listSeparatorSendIm_edit">"即時通訊地址"</string>
+ <string name="listSeparatorMapAddress">"在地圖上顯示地址"</string>
+ <string name="listSeparatorMapAddress_edit">"聯絡地址"</string>
+ <string name="listSeparatorOrganizations">"組織"</string>
+ <string name="listSeparatorOtherInformation">"其他資訊"</string>
+ <string name="listSeparatorOtherInformation_edit">"其他選項"</string>
+ <string name="listSeparatorMore_edit">"更多"</string>
+ <string name="contactsIconLabel">"連絡人"</string>
+ <string name="contactsFavoritesLabel">"我的最愛"</string>
+ <string name="dialerIconLabel">"撥號"</string>
+ <string name="recentCallsIconLabel">"通話紀錄"</string>
+ <string name="liveFolderAll">"全部聯絡人"</string>
+ <string name="liveFolderFavorites">"有星號標記的聯絡人"</string>
+ <string name="liveFolderPhone">"有電話號碼的聯絡人"</string>
+ <string name="menu_sendTextMessage">"傳送 SMS 訊息"</string>
+ <string name="recentCalls_callNumber">"撥打電話給 <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="recentCalls_editNumberBeforeCall">"撥打電話前編輯號碼"</string>
+ <string name="recentCalls_addToContact">"新增至連絡人"</string>
+ <string name="recentCalls_removeFromRecentList">"從通話紀錄中移除"</string>
+ <string name="recentCalls_deleteAll">"清除通話紀錄"</string>
+ <string name="recentCalls_empty">"通話紀錄是空的。"</string>
+ <string name="imei">"IMEI"</string>
+ <string name="voicemail">"語音信箱"</string>
+ <string name="unknown">"未知的"</string>
+ <string name="private_num">"私人號碼"</string>
+ <string name="dialerKeyboardHintText">"使用鍵盤撥號"</string>
+ <string name="dialerDialpadHintText">"撥號即可新增通話"</string>
+ <string name="simContacts_emptyLoading">"從 SIM 卡讀取中…"</string>
+ <string name="simContacts_title">"SIM 卡連絡人"</string>
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">" 同步處理您的 Google 連絡人!"</font>" "\n" 與手機同步處理後,無論您走到哪,都可存取連絡人資訊。"</string>
+ <!-- no translation found for noContactsHelpText (2249195687463896364) -->
+ <skip />
+ <!-- no translation found for noContactsHelpTextWithSync (4927701496550314555) -->
+ <skip />
+ <string name="seclectSyncGroups_title">"選取要同步處理的群組"</string>
+ <string name="liveFolder_all_label">"全部聯絡人"</string>
+ <string name="liveFolder_favorites_label">"星號標記"</string>
+ <string name="liveFolder_phones_label">"電話"</string>
+ <string name="dialer_useDtmfDialpad">"使用按鍵式鍵盤"</string>
+ <string name="dialer_returnToInCallScreen">"返回進行中的通話"</string>
+ <string name="dialer_addAnotherCall">"新增通話"</string>
+ <string name="callDetailTitle">"通話詳細資料"</string>
+ <string name="toast_call_detail_error">"無法解讀通話要求詳細資訊。"</string>
+ <string name="call_type">"類型"</string>
+ <string name="type_incoming">"外來通話"</string>
+ <string name="type_outgoing">"播出通話"</string>
+ <string name="type_missed">"未接通話"</string>
+ <string name="call_time">"時間"</string>
+ <string name="datetime_relative">"<xliff:g id="DATETIME">%1$s</xliff:g> (<xliff:g id="RELATIVE">%2$s</xliff:g>)"</string>
+ <string name="call_duration">"持續時間"</string>
+ <string name="header_actions">"動作"</string>
+ <string name="ringtone_spinner">"鈴聲:<xliff:g id="RINGTONE">%1$s</xliff:g>"</string>
+ <string name="add_more_items">"新增更多項目"</string>
+ <string name="actionCall">"播打<xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionEmail">"傳送電子郵件至<xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionText">"傳送簡訊至<xliff:g id="TYPE">%s</xliff:g>"</string>
+ <string name="actionChat">"透過 <xliff:g id="TYPE">%s</xliff:g> 進行即時通訊"</string>
+ <string name="actionMap">"檢視<xliff:g id="TYPE">%s</xliff:g>地址"</string>
+ <string name="actionIncomingCall">"來電"</string>
+ <string name="detailIncomingCallsGoToVoicemail">"來電將直接轉到語音信箱"</string>
+ <string name="detailsRingtone">"設為<xliff:g id="RINGTONE_NAME">%s</xliff:g>"</string>
+ <string name="callBack">"回播電話"</string>
+ <!-- no translation found for callAgain (3197312117049874778) -->
+ <skip />
+ <!-- no translation found for returnCall (8171961914203617813) -->
+ <skip />
+ <!-- no translation found for callDetailsDurationFormat (8157706382818184268) -->
+ <skip />
+</resources>
diff --git a/res/values/ids.xml b/res/values/ids.xml
new file mode 100644
index 000000000..fb1fe41a4
--- /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 000000000..b63d8c86f
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,483 @@
+<?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">
+ <!-- Title for the activity that shows contacts. This is the name
+ used in the Launcher icon. -->
+ <string name="contactsList">Contacts</string>
+
+ <!-- Title for the activity that dials the phone. This is the name
+ used in the Launcher icon. -->
+ <string name="launcherDialer">Dialer</string>
+
+ <!-- Name of activity that allows users to create shortcuts on the home screen to a contact.
+ This shows up in a list of things like bookmark, folder, music playlist, etc -->
+ <string name="shortcutContact">Contact</string>
+
+ <!-- Title for the activity that shows only starred contacts -->
+ <string name="starredList">Starred</string>
+
+ <!-- Title for the activity that shows only frequently contacted contacts -->
+ <string name="frequentList">Frequent</string>
+
+ <!-- Title for the activity that shows a mix of starred contacts and frequently contacted
+ contacts. -->
+ <string name="strequentList">Favorites</string>
+
+ <!-- The title bar when viewing the contact details activity -->
+ <string name="viewContactTitle">Contact details</string>
+
+ <!-- The description presented to the user in the Intent choose when there are multiple activities that allow
+ viewing a contact. This string represents the built in way to view the contact. -->
+ <string name="viewContactDesription">View contact</string>
+
+ <!-- The description presented to the user in the Intent choose when there are multiple activities that allow
+ editing a contact. This string represents the built in way to edit the contact. -->
+ <string name="editContactDescription">Edit contact</string>
+
+ <!-- The description presented to the user in the Intent choose when there are multiple activities that allow
+ creating a new contact. This string represents the built in way to create the contact. -->
+ <string name="insertContactDescription">Create contact</string>
+
+ <!-- Hint text in the search box when the user hits the Search key while in the contacts app -->
+ <string name="searchHint">Search contacts</string>
+
+ <!-- Menu item to search contacts -->
+ <string name="menu_search">Search</string>
+
+ <!-- Menu item to create a new contact -->
+ <string name="menu_newContact">New contact</string>
+
+ <!-- Menu item used to view the details for a specific contact -->
+ <string name="menu_viewContact">View contact</string>
+
+ <!-- Menu item used to call a contact, containing the name of the contact to call -->
+ <string name="menu_callNumber">Call <xliff:g id="name">%s</xliff:g></string>
+
+ <!-- Menu item used to add a star to a contact, which makes that contact show up at the top of favorites -->
+ <string name="menu_addStar">Add to favorites</string>
+
+ <!-- Menu item used to remove a star from a contact, making the contact no longer show up at the top of favorites -->
+ <string name="menu_removeStar">Remove from favorites</string>
+
+ <!-- Optional menu item to encode the current contact as a QR Code using Barcode Scanner. -->
+ <string name="menu_showBarcode">Show barcode</string>
+
+ <!-- Menu item used to edit a specific contact -->
+ <string name="menu_editContact">Edit contact</string>
+
+ <!-- Menu item used to delete a specific contact -->
+ <string name="menu_deleteContact">Delete contact</string>
+
+ <!-- Menu item used to call a specific contact when viewing the details of that contact. -->
+ <string name="menu_call">Call</string>
+
+ <!-- Menu item used to send an SMS or MMS message to a specific phone number or a contacts default phone number -->
+ <string name="menu_sendSMS">Send SMS/MMS</string>
+
+ <!-- Menu item used to send an email message to a specific email address -->
+ <string name="menu_sendEmail">Send email</string>
+
+ <!-- Menu item used to view a contact's address on a map -->
+ <string name="menu_viewAddress">Map address</string>
+
+ <!-- Menu item that makes a phone the default for a contact. The default number used when you
+ try to call a contact without specifying a specific number. -->
+ <string name="menu_makeDefaultNumber">Make default number</string>
+
+ <!-- Confirmation dialog title after users selects to delete a contact. -->
+ <string name="deleteConfirmation_title">Delete</string>
+
+ <!-- Confirmation dialog contents after users selects to delete a contact. -->
+ <string name="deleteConfirmation">This contact will be deleted.</string>
+
+ <!-- Menu item to indicate you are done editing a contact and want to save the changes you've made -->
+ <string name="menu_done">Done</string>
+
+ <!-- Menu item to indicate you want to stop editing a contact and NOT save the changes you've made -->
+ <string name="menu_doNotSave">Revert</string>
+
+ <!-- The title of the activity that edits and existing contact -->
+ <string name="editContact_title_edit">Edit contact</string>
+
+ <!-- The title of the activity that creates a new contact -->
+ <string name="editContact_title_insert">New contact</string>
+
+ <!-- Button that adds more info to a contact, like a new phone number, email address, or postal address -->
+ <string name="menu_addItem">More info</string>
+
+ <!-- The label describing the phonetic pronunciation/reading of a contact name -->
+ <string name="label_phonetic_name">Phonetic</string>
+
+ <!-- The label describing the Notes field of a contact. This field allows free form text entry about a contact -->
+ <string name="label_notes">Notes</string>
+
+ <!-- The label describing the custom ringtone for a contact -->
+ <string name="label_ringtone">Ringtone</string>
+
+ <!-- Hint text for the contact name when editing -->
+ <string name="ghostData_name">First and Last</string>
+
+ <!-- Hint text for the phonetic reading of the contact name when editing -->
+ <string name="ghostData_phonetic_name">Phonetic name</string>
+
+ <!-- Hint text for the organization name when editing -->
+ <string name="ghostData_company">Company</string>
+
+ <!-- Hint text for the organization position when editing -->
+ <string name="ghostData_title">Position</string>
+
+ <!-- Hint text for an IM account when editing -->
+ <string name="ghostData_im">IM name</string>
+
+ <!-- Hint text for the notes field when editing -->
+ <string name="ghostData_notes">My note</string>
+
+ <!-- Hint text for the phone number field when editing -->
+ <string name="ghostData_phone">Phone number</string>
+
+ <!-- Hint text for the email address field when editing -->
+ <string name="ghostData_email">Email address</string>
+
+ <!-- Hint text for the postal address field when editing -->
+ <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>
+
+ <!-- When picking a contact from a list of all contacts there is an entry at the top of the
+ list that allows the user to create a new contact, which this string is used for -->
+ <string name="pickerNewContactHeader">Create new contact</string>
+
+ <!-- Dialog title when you select a label when creating or edit a contact -->
+ <string name="selectLabel">Select label</string>
+
+ <!-- Header that expands to list all of the types of phone numbers when editing or creating a phone number for a contact -->
+ <string name="phoneLabelsGroup">Phone</string>
+
+ <!-- Header that expands to list all of the types of email addresses when editing or creating an email address for a contact -->
+ <string name="emailLabelsGroup">Email</string>
+
+ <!-- Header that expands to list all of the types of IM account when editing or creating an IM account for a contact -->
+ <string name="imLabelsGroup">IM</string>
+
+ <!-- Header that expands to list all of the types of postal addresses when editing or creating an postal address for a contact -->
+ <string name="postalLabelsGroup">Postal address</string>
+
+ <!-- Header that expands to list other types of data you can add to a contact, like notes and organizations -->
+ <string name="otherLabelsGroup">Other</string>
+
+ <!-- The order of the items below is important, don't reorder without changing EditContactActivity.java -->
+ <skip/>
+ <!-- The labels that are under the otherLabelsGroup when editing a contact. -->
+ <string-array name="otherLabels">
+ <!-- An organization associated with a contact -->
+ <item>Organization</item>
+ <!-- A note associated with a contact -->
+ <item>Note</item>
+ </string-array>
+
+ <!-- Title of the dialog that appears if there are no pictures from which to create an icon for a contact -->
+ <string name="errorDialogTitle">No pictures</string>
+
+ <!-- Description in the dialog that appears if there are no pictures from which to create an icon for a contact -->
+ <string name="photoPickerNotFoundText">No pictures are available on the phone.</string>
+
+ <!-- Description of the activity used to set a photo in the pictures application as the icon for a contact -->
+ <string name="attachToContact">Contact icon</string>
+
+ <!-- Title of the dialog used to set a custom label for a contact detail, like a phone number or email address.
+ For example, this may be used to set a phone number's label to "Vaction house" -->
+ <string name="customLabelPickerTitle">Custom label name</string>
+
+ <!-- The menu item to open the list of groups to display -->
+ <string name="menu_displayGroup">Display group</string>
+
+ <!-- The menu item that leads to the settings for contact syncing -->
+ <string name="syncGroupPreference">Edit sync groups</string>
+
+ <!-- The menu item that launches the SIM card import activity -->
+ <string name="importFromSim">Import contacts</string>
+
+ <!-- Check box label that allows calls to the contact to be sent directly to voicemail -->
+ <string name="send_to_voicemail_checkbox">Send calls directly to voicemail</string>
+
+ <!-- String letting the user know that calls from the contact they're viewing will be sent directly to voicemail -->
+ <string name="send_to_voicemail_view">Calls are sent directly to voicemail.</string>
+
+ <!-- String used to indicate that a contact doesn't have a custom ringtone -->
+ <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 dialog. The dialog contains a list of contact groups that the
+ user can pick from, indicating they only want to see the contacts in that group. -->
+ <string name="select_group_title">Groups</string>
+
+ <!-- The text displayed when the contacts list is empty while displaying a single group of contacts -->
+ <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>
+
+ <!-- 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>
+
+ <!-- Section header in the Edit Contacts screen for phone numbers -->
+ <string name="listSeparatorCallNumber_edit">Phone numbers</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>
+
+ <!-- Section header in the Edit Contacts screen for E-mail addresses -->
+ <string name="listSeparatorSendEmail_edit">Email addresses</string>
+
+ <!-- Separator in the contact details list describing that the items below it will send an IM -->
+ <string name="listSeparatorSendIm">Send instant message</string>
+
+ <!-- Section header in the Edit Contacts screen for Instant messanger accounts -->
+ <string name="listSeparatorSendIm_edit">Chat addresses</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>
+
+ <!-- Section header in the Edit Contacts screen for map addresses -->
+ <string name="listSeparatorMapAddress_edit">Postal addresses</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>
+
+ <!-- Section header in the Edit Contacts screen for other options, such as setting ringtone -->
+ <string name="listSeparatorOtherInformation_edit">Other options</string>
+
+ <!-- Section header in the Edit Contacts screen for the "Add more items" button -->
+ <string name="listSeparatorMore_edit">More</string>
+
+ <!-- The description text for the contacts tab. Space is limited for this string, so the shorter the better -->
+ <string name="contactsIconLabel">Contacts</string>
+
+ <!-- The description text for the favorites tab. Space is limited for this string, so the shorter the better -->
+ <string name="contactsFavoritesLabel">Favorites</string>
+
+ <!-- The description text for the dialer tab. Space is limited for this string, so the shorter the better -->
+ <string name="dialerIconLabel">Dialer</string>
+
+ <!-- The description text for the call log tab. Space is limited for this string, so the shorter the better -->
+ <string name="recentCallsIconLabel">Call log</string>
+ <string name="liveFolderAll">All contacts</string>
+ <string name="liveFolderFavorites">Starred contacts</string>
+ <string name="liveFolderPhone">Contacts with phone numbers</string>
+
+ <!-- Menu item used to send an SMS or MMS message to a phone number -->
+ <string name="menu_sendTextMessage">Send SMS message</string>
+
+ <!-- Menu item used to call a contact from the call log -->
+ <string name="recentCalls_callNumber">Call <xliff:g id="name">%s</xliff:g></string>
+
+ <!-- Menu item used to copy a number from the call log to the dialer so it can be edited before calling it -->
+ <string name="recentCalls_editNumberBeforeCall">Edit number before call</string>
+
+ <!-- Menu item used to add a number from the call log to contacts -->
+ <string name="recentCalls_addToContact">Add to contacts</string>
+
+ <!-- Menu item used to remove a single call from the call log -->
+ <string name="recentCalls_removeFromRecentList">Remove from call log</string>
+
+ <!-- Menu item used to remove all calls from the call log -->
+ <string name="recentCalls_deleteAll">Clear call log</string>
+
+ <!-- Text displayed when the call log is empty -->
+ <string name="recentCalls_empty">Call log is empty.</string>
+
+ <!-- The title of a dialog that displays the IMEI of the phone -->
+ <string name="imei">IMEI</string>
+
+ <!-- String used for displaying calls to the voicemail number in the call log -->
+ <string name="voicemail">Voicemail</string>
+
+ <!-- String used to display calls from unknown numbers in the call log -->
+ <string name="unknown">Unknown</string>
+
+ <!-- String used to display calls from private numbers in the call log -->
+ <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>
+
+ <!-- Hint text displayed in the "digits" field above the dialer's
+ dialpad, if there's already a call in progress. (This hint
+ reminds the user that the dialer will add a new call, as opposed
+ to sending DTMF tones over the current call.) -->
+ <string name="dialerDialpadHintText">Dial to add a call</string>
+
+ <!-- Dialog text displayed when loading a phone number from the SIM card for speed dial -->
+ <string name="simContacts_emptyLoading">Loading from SIM card\u2026</string>
+
+ <!-- Dialog title displayed when loading a phone number from the SIM card for speed dial -->
+ <string name="simContacts_title">SIM card contacts</string>
+
+ <!-- Displayed when a user is prompted to setup a Google account for syncing -->
+ <string name="contactsSyncPlug"><font fgcolor="#ffffffff">Sync your Google contacts!</font>
+ \nAfter syncing to your phone, your contacts will be available to you wherever you go.</string>
+
+ <!-- Displayed full screen when the user has no contacts and they are displaying the My Contacts group, and contact syncing is disabled -->
+ <string name="noContactsHelpText">"You don't have any contacts.\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and select:\n
+ \n<li><font fgcolor="#ffffffff"><b>New contact</b></font> to create a new contact from scratch\n</li>
+ \n<li><font fgcolor="#ffffffff"><b>Import contacts</b></font> to add contacts from your SIM card\n</li>"
+ </string>
+
+ <!-- Displayed full screen when the user has no contacts and they are displaying the My Contacts group, and contact syncing is enabled -->
+ <string name="noContactsHelpTextWithSync">"You don't have any contacts.\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and select:\n
+ \n<li><font fgcolor="#ffffffff"><b>Edit sync groups</b></font> to add from a new or existing Google account\n</li>
+ \n<li><font fgcolor="#ffffffff"><b>New contact</b></font> to create a new contact from scratch\n</li>
+ \n<li><font fgcolor="#ffffffff"><b>Import contacts</b></font> to add contacts from your SIM card\n</li>"
+ </string>
+
+ <!-- Activity title for the activity that lets the user choose which groups of contacts to sync from the server -->
+ <string name="seclectSyncGroups_title">Select groups to sync</string>
+
+ <!-- Live folder label for all contacts -->
+ <string name="liveFolder_all_label">All Contacts</string>
+
+ <!-- Live folder label for only starred contacts -->
+ <string name="liveFolder_favorites_label">Starred</string>
+
+ <!-- Live folder label for all contacts with phone numbers -->
+ <string name="liveFolder_phones_label">Phones</string>
+
+ <!-- Item label: jump to the in-call DTMF dialpad.
+ (Part of a list of options shown in the dialer when another call
+ is already in progress.) -->
+ <string name="dialer_useDtmfDialpad">Use touch tone keypad</string>
+
+ <!-- Item label: jump to the in-call UI.
+ (Part of a list of options shown in the dialer when another call
+ is already in progress.) -->
+ <string name="dialer_returnToInCallScreen">Return to call in progress</string>
+
+ <!-- Item label: use the Dialer's dialpad to add another call.
+ (Part of a list of options shown in the dialer when another call
+ is already in progress.) -->
+ <string name="dialer_addAnotherCall">Add call</string>
+
+ <!-- Title bar for call detail screen -->
+ <string name="callDetailTitle">Call details</string>
+
+ <!-- Toast for call detail screen when couldn't read the requested details -->
+ <string name="toast_call_detail_error">Couldn\'t read details for the requested call.</string>
+
+ <!-- In call detail screen, label for call type -->
+ <string name="call_type">Type</string>
+
+ <!-- Title for incoming call details screen -->
+ <string name="type_incoming">Incoming call</string>
+
+ <!-- Title for outgoing call details screen -->
+ <string name="type_outgoing">Outgoing call</string>
+
+ <!-- Title for missed call details screen -->
+ <string name="type_missed">Missed call</string>
+
+ <!-- In call detail screen, label for call date/time -->
+ <string name="call_time">Time</string>
+
+ <!-- In call detail screen, combine date/time string and relative time to give verbose call date/time -->
+ <string name="datetime_relative">"<xliff:g id="datetime" example="Dec 31, 2007, 11:00 am">%1$s</xliff:g> (<xliff:g id="relative" example="4 hours ago">%2$s</xliff:g>)"</string>
+
+ <!-- In call detail screen, label for call duration -->
+ <string name="call_duration">Duration</string>
+
+ <!-- In call detail screen, header title for actions section -->
+ <string name="header_actions">Actions</string>
+
+ <!-- In edit contact screen, combine the actual ringtone name with prefix for placement in spinner -->
+ <string name="ringtone_spinner">"Ringtone: <xliff:g id="ringtone" example="Caribbean Ice">%1$s</xliff:g>"</string>
+
+ <!-- In edit contact screen, add more info spinner title -->
+ <string name="add_more_items">Add more items</string>
+
+ <!-- Action string for calling a phone number. -->
+ <string name="actionCall">Call <xliff:g id="type" example="mobile">%s</xliff:g></string>
+
+ <!-- Action string for sending an email to an email address. -->
+ <string name="actionEmail">Email <xliff:g id="type" example="home">%s</xliff:g></string>
+
+ <!-- Action string for sending a text message to a phone number. -->
+ <string name="actionText">Text <xliff:g id="type" example="mobile">%s</xliff:g></string>
+
+ <!-- Action string for sending an instant message to a chat buddy. -->
+ <string name="actionChat">Chat using <xliff:g id="type" example="Google Talk">%s</xliff:g></string>
+
+ <!-- Action string for viewing an address on a map. -->
+ <string name="actionMap">View <xliff:g id="type" example="home">%s</xliff:g> address</string>
+
+ <!-- Description for incoming calls going to voice mail vs. not -->
+ <string name="actionIncomingCall">Incoming calls</string>
+
+ <!-- Detail text for incoming calls going to voice mail -->
+ <string name="detailIncomingCallsGoToVoicemail">Will be sent directly to voicemail</string>
+
+ <!-- Description of what the contact specific ringtone is set to -->
+ <string name="detailsRingtone">Set to <xliff:g id="ringtone_name" example="Flutey Phone">%s</xliff:g></string>
+
+ <!-- Action string for calling back a number in the call log -->
+ <string name="callBack">Call back</string>
+
+ <!-- Action string for calling a number in the call log again -->
+ <string name="callAgain">Call again</string>
+
+ <!-- Action string for returning a missed call in the call log -->
+ <string name="returnCall">Return call</string>
+
+ <!-- A nicely formatted call duration displayed when viewing call details. For example "42 mins 28 secs" -->
+ <string name="callDetailsDurationFormat"><xliff:g id="minutes" example="42">%s</xliff:g> mins <xliff:g id="seconds" example="28">%s</xliff:g> secs</string>
+</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 000000000..e43842c6e
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+ <style name="MinusButton">
+ <item name="android:background">@drawable/btn_circle</item>
+ <item name="android:src">@drawable/ic_btn_round_minus</item>
+ </style>
+
+ <style name="PlusButton">
+ <item name="android:background">@drawable/btn_circle</item>
+ <item name="android:src">@drawable/ic_btn_round_plus</item>
+ </style>
+
+ <style name="MoreButton">
+ <item name="android:background">@drawable/btn_circle</item>
+ <item name="android:src">@drawable/ic_btn_round_more</item>
+ </style>
+
+</resources>
diff --git a/res/xml/searchable.xml b/res/xml/searchable.xml
new file mode 100644
index 000000000..cd9e6d6a1
--- /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/AttachImage.java b/src/com/android/contacts/AttachImage.java
new file mode 100644
index 000000000..8c917221e
--- /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.
+ */
+public 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 000000000..e3431b155
--- /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/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
new file mode 100644
index 000000000..012a33ef6
--- /dev/null
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts;
+
+import android.app.ListActivity;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.CallLog;
+import android.provider.Contacts;
+import android.provider.CallLog.Calls;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.Contacts.Intents.Insert;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Displays the details of a specific call log entry.
+ */
+public class CallDetailActivity extends ListActivity implements
+ AdapterView.OnItemClickListener {
+ private static final String TAG = "CallDetail";
+
+ private TextView mCallType;
+ private ImageView mCallTypeIcon;
+ private TextView mCallTime;
+ private TextView mCallDuration;
+
+ private String mNumber = null;
+
+ /* package */ LayoutInflater mInflater;
+ /* package */ Resources mResources;
+
+ static final String[] CALL_LOG_PROJECTION = new String[] {
+ CallLog.Calls.DATE,
+ CallLog.Calls.DURATION,
+ CallLog.Calls.NUMBER,
+ CallLog.Calls.TYPE,
+ };
+
+ static final int DATE_COLUMN_INDEX = 0;
+ static final int DURATION_COLUMN_INDEX = 1;
+ static final int NUMBER_COLUMN_INDEX = 2;
+ static final int CALL_TYPE_COLUMN_INDEX = 3;
+
+ static final String[] PHONES_PROJECTION = new String[] {
+ Phones.PERSON_ID,
+ Phones.DISPLAY_NAME,
+ Phones.TYPE,
+ Phones.LABEL,
+ Phones.NUMBER,
+ };
+ static final int COLUMN_INDEX_ID = 0;
+ static final int COLUMN_INDEX_NAME = 1;
+ static final int COLUMN_INDEX_TYPE = 2;
+ static final int COLUMN_INDEX_LABEL = 3;
+ static final int COLUMN_INDEX_NUMBER = 4;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setContentView(R.layout.call_detail);
+
+ mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
+ mResources = getResources();
+
+ mCallType = (TextView) findViewById(R.id.type);
+ mCallTypeIcon = (ImageView) findViewById(R.id.icon);
+ mCallTime = (TextView) findViewById(R.id.time);
+ mCallDuration = (TextView) findViewById(R.id.duration);
+
+ getListView().setOnItemClickListener(this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateData(getIntent().getData());
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_CALL: {
+ // Make sure phone isn't already busy before starting direct call
+ TelephonyManager tm = (TelephonyManager)
+ getSystemService(Context.TELEPHONY_SERVICE);
+ if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
+ Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+ Uri.fromParts("tel", mNumber, null));
+ startActivity(callIntent);
+ return true;
+ }
+ }
+ }
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ /**
+ * Update user interface with details of given call.
+ *
+ * @param callUri Uri into {@link CallLog.Calls}
+ */
+ private void updateData(Uri callUri) {
+ ContentResolver resolver = getContentResolver();
+ Cursor callCursor = resolver.query(callUri, CALL_LOG_PROJECTION, null, null, null);
+ try {
+ if (callCursor != null && callCursor.moveToFirst()) {
+ // Read call log specifics
+ mNumber = callCursor.getString(NUMBER_COLUMN_INDEX);
+ long date = callCursor.getLong(DATE_COLUMN_INDEX);
+ long duration = callCursor.getLong(DURATION_COLUMN_INDEX);
+ int callType = callCursor.getInt(CALL_TYPE_COLUMN_INDEX);
+
+ // Pull out string in format [relative], [date]
+ CharSequence dateClause = DateUtils.formatDateRange(this, date, date,
+ DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
+ DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR);
+ mCallTime.setText(dateClause);
+
+ // Set the duration
+ if (callType == Calls.MISSED_TYPE) {
+ mCallDuration.setVisibility(View.GONE);
+ } else {
+ mCallDuration.setVisibility(View.VISIBLE);
+ mCallDuration.setText(formatDuration(duration));
+ }
+
+ // Set the call type icon and caption
+ String callText = null;
+ switch (callType) {
+ case Calls.INCOMING_TYPE:
+ mCallTypeIcon.setImageResource(android.R.drawable.sym_call_incoming);
+ mCallType.setText(R.string.type_incoming);
+ callText = getString(R.string.callBack);
+ break;
+
+ case Calls.OUTGOING_TYPE:
+ mCallTypeIcon.setImageResource(android.R.drawable.sym_call_outgoing);
+ mCallType.setText(R.string.type_outgoing);
+ callText = getString(R.string.callAgain);
+ break;
+
+ case Calls.MISSED_TYPE:
+ mCallTypeIcon.setImageResource(android.R.drawable.sym_call_missed);
+ mCallType.setText(R.string.type_missed);
+ callText = getString(R.string.returnCall);
+ break;
+ }
+
+ // Perform a reverse-phonebook lookup to find the PERSON_ID
+ String callLabel = null;
+ Uri personUri = null;
+ Uri phoneUri = Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, Uri.encode(mNumber));
+ Cursor phonesCursor = resolver.query(phoneUri, PHONES_PROJECTION, null, null, null);
+ try {
+ if (phonesCursor != null && phonesCursor.moveToFirst()) {
+ long personId = phonesCursor.getLong(COLUMN_INDEX_ID);
+ personUri = ContentUris.withAppendedId(
+ Contacts.People.CONTENT_URI, personId);
+ callText = getString(R.string.recentCalls_callNumber,
+ phonesCursor.getString(COLUMN_INDEX_NAME));
+ mNumber = phonesCursor.getString(COLUMN_INDEX_NUMBER);
+ callLabel = Phones.getDisplayLabel(this,
+ phonesCursor.getInt(COLUMN_INDEX_TYPE),
+ phonesCursor.getString(COLUMN_INDEX_LABEL)).toString();
+ } else {
+ mNumber = PhoneNumberUtils.formatNumber(mNumber);
+ }
+ } finally {
+ if (phonesCursor != null) phonesCursor.close();
+ }
+
+ // Build list of various available actions
+ List<ViewEntry> actions = new ArrayList<ViewEntry>();
+
+ Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+ Uri.fromParts("tel", mNumber, null));
+ ViewEntry entry = new ViewEntry(android.R.drawable.sym_action_call, callText,
+ callIntent);
+ entry.number = mNumber;
+ entry.label = callLabel;
+ actions.add(entry);
+
+ Intent smsIntent = new Intent(Intent.ACTION_SENDTO,
+ Uri.fromParts("sms", mNumber, null));
+ actions.add(new ViewEntry(R.drawable.sym_action_sms,
+ getString(R.string.menu_sendTextMessage), smsIntent));
+
+ // Let user view contact details if they exist, otherwise add option
+ // to create new contact from this number.
+ if (personUri != null) {
+ Intent viewIntent = new Intent(Intent.ACTION_VIEW, personUri);
+ actions.add(new ViewEntry(R.drawable.sym_action_view_contact,
+ getString(R.string.menu_viewContact), viewIntent));
+ } else {
+ Intent createIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ createIntent.setType(People.CONTENT_ITEM_TYPE);
+ createIntent.putExtra(Insert.PHONE, mNumber);
+ actions.add(new ViewEntry(R.drawable.sym_action_add,
+ getString(R.string.recentCalls_addToContact), createIntent));
+ }
+
+ ViewAdapter adapter = new ViewAdapter(this, actions);
+ setListAdapter(adapter);
+ } else {
+ // Something went wrong reading in our primary data, so we're going to
+ // bail out and show error to users.
+ Toast.makeText(this, R.string.toast_call_detail_error,
+ Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ } finally {
+ if (callCursor != null) {
+ callCursor.close();
+ }
+ }
+ }
+
+ private String formatDuration(long elapsedSeconds) {
+ long minutes = 0;
+ long seconds = 0;
+
+ if (elapsedSeconds >= 60) {
+ minutes = elapsedSeconds / 60;
+ elapsedSeconds -= minutes * 60;
+ }
+ seconds = elapsedSeconds;
+
+ return getString(R.string.callDetailsDurationFormat, minutes, seconds);
+ }
+
+ static final class ViewEntry {
+ public int icon = -1;
+ public String text = null;
+ public Intent intent = null;
+ public String label = null;
+ public String number = null;
+
+ public ViewEntry(int icon, String text, Intent intent) {
+ this.icon = icon;
+ this.text = text;
+ this.intent = intent;
+ }
+ }
+
+ static final class ViewAdapter extends BaseAdapter {
+
+ private final List<ViewEntry> mActions;
+
+ private final LayoutInflater mInflater;
+
+ public ViewAdapter(Context context, List<ViewEntry> actions) {
+ mActions = actions;
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ public int getCount() {
+ return mActions.size();
+ }
+
+ public Object getItem(int position) {
+ return mActions.get(position);
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // Make sure we have a valid convertView to start with
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.call_detail_list_item, parent, false);
+ }
+
+ // Fill action with icon and text.
+ ViewEntry entry = mActions.get(position);
+ convertView.setTag(entry);
+
+ ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
+ TextView text = (TextView) convertView.findViewById(android.R.id.text1);
+
+ icon.setImageResource(entry.icon);
+ text.setText(entry.text);
+
+ View line2 = convertView.findViewById(R.id.line2);
+ boolean numberEmpty = TextUtils.isEmpty(entry.number);
+ boolean labelEmpty = TextUtils.isEmpty(entry.label) || numberEmpty;
+ if (labelEmpty && numberEmpty) {
+ line2.setVisibility(View.GONE);
+ } else {
+ line2.setVisibility(View.VISIBLE);
+
+ TextView label = (TextView) convertView.findViewById(R.id.label);
+ if (labelEmpty) {
+ label.setVisibility(View.GONE);
+ } else {
+ label.setText(entry.label);
+ label.setVisibility(View.VISIBLE);
+ }
+
+ TextView number = (TextView) convertView.findViewById(R.id.number);
+ number.setText(entry.number);
+ }
+
+ return convertView;
+ }
+ }
+
+ public void onItemClick(AdapterView parent, View view, int position, long id) {
+ // Handle passing action off to correct handler.
+ if (view.getTag() instanceof ViewEntry) {
+ ViewEntry entry = (ViewEntry) view.getTag();
+ if (entry.intent != null) {
+ startActivity(entry.intent);
+ }
+ }
+ }
+}
diff --git a/src/com/android/contacts/ContactEntryAdapter.java b/src/com/android/contacts/ContactEntryAdapter.java
new file mode 100644
index 000000000..c5b7ccfc4
--- /dev/null
+++ b/src/com/android/contacts/ContactEntryAdapter.java
@@ -0,0 +1,341 @@
+/*
+ * 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
+ People.PHONETIC_NAME, // 8
+ };
+ 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 int CONTACT_PHONETIC_NAME_COLUMN = 8;
+
+ 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 000000000..80b516686
--- /dev/null
+++ b/src/com/android/contacts/ContactsGroupSyncSelector.java
@@ -0,0 +1,256 @@
+/*
+ * 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 com.google.android.googlelogin.GoogleLoginServiceConstants;
+import com.google.android.googlelogin.GoogleLoginServiceHelper;
+
+import android.app.ListActivity;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.Contacts;
+import android.provider.Gmail;
+import android.provider.Contacts.Groups;
+import android.provider.Contacts.Settings;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+public final class ContactsGroupSyncSelector extends ListActivity implements View.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 static final int SUBACTIVITY_GET_ACCOUNT = 1;
+
+ boolean[] mChecked;
+ boolean mSyncAllGroups;
+ long[] mGroupIds;
+
+ private final class GroupsAdapter extends ArrayAdapter<CharSequence> {
+ public GroupsAdapter(CharSequence[] items) {
+ super(ContactsGroupSyncSelector.this,
+ 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
+ */
+ @Override
+ protected void onListItemClick(ListView list, View view, int position, long id) {
+ boolean isChecked = list.isItemChecked(position);
+ mChecked[position] = isChecked;
+ if (position == 0) {
+ mSyncAllGroups = isChecked;
+ adjustChecks();
+ }
+ }
+
+ /**
+ * Handles clicks on the OK and cancel buttons
+ */
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.cancel: {
+ finish();
+ break;
+ }
+
+ case R.id.ok: {
+ // The list isn't setup yet, so just return without doing anything.
+ if (mChecked == null) {
+ finish();
+ return;
+ }
+
+ final ContentResolver resolver = getContentResolver();
+ 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(resolver, null, Settings.SYNC_EVERYTHING, "1");
+ } else {
+ 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");
+ }
+ finish();
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ // Only look for an account on first run.
+ if (savedState == null) {
+ // This will request a Gmail account and if none are present, it will
+ // invoke SetupWizard to login or create one. The result is returned
+ // through onActivityResult().
+ Bundle bundle = new Bundle();
+ bundle.putCharSequence("optional_message", getText(R.string.contactsSyncPlug));
+ GoogleLoginServiceHelper.getCredentials(this, SUBACTIVITY_GET_ACCOUNT,
+ bundle, GoogleLoginServiceConstants.PREFER_HOSTED, Gmail.GMAIL_AUTH_SERVICE,
+ true);
+ }
+
+ setContentView(R.layout.sync_settings);
+
+ findViewById(R.id.ok).setOnClickListener(this);
+ findViewById(R.id.cancel).setOnClickListener(this);
+
+ getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
+ if (requestCode == SUBACTIVITY_GET_ACCOUNT) {
+ if (resultCode == RESULT_OK) {
+ // There is an account setup, build the group list
+ buildItems();
+ adjustChecks();
+ } else {
+ finish();
+ }
+ }
+ }
+
+ private void buildItems() {
+ final ContentResolver resolver = getContentResolver();
+ Cursor cursor = resolver.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(resolver);
+ checked[0] = mSyncAllGroups;
+ mGroupIds = groupIds;
+
+ // Setup the adapter
+ setListAdapter(new GroupsAdapter(items));
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+
+ private void adjustChecks() {
+ final ListView list = getListView();
+ 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 000000000..6e72fbd51
--- /dev/null
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -0,0 +1,1518 @@
+/*
+ * 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.IContentProvider;
+import android.content.ISyncAdapter;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.CharArrayBuffer;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.RemoteException;
+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.text.TextUtils;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.Gravity;
+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.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.AlphabetIndexer;
+import android.widget.Filter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.ResourceCursorAdapter;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+
+import java.lang.ref.WeakReference;
+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_SEARCH = 1;
+ 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;
+
+ /**
+ * 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";
+
+
+ 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;
+
+ 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;
+ private boolean mSyncEnabled;
+
+ /**
+ * 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);
+
+ // 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;
+ }
+
+ // 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);
+ setListAdapter(mAdapter);
+
+ // We manually save/restore the listview state
+ list.setSaveEnabled(false);
+
+ mQueryHandler = new QueryHandler(this);
+ mJustCreated = true;
+
+ // Check to see if sync is enabled
+ final ContentResolver resolver = getContentResolver();
+ IContentProvider provider = resolver.acquireProvider(Contacts.CONTENT_URI);
+ if (provider == null) {
+ // No contacts provider, bail.
+ finish();
+ return;
+ }
+
+ try {
+ ISyncAdapter sa = provider.getSyncAdapter();
+ mSyncEnabled = sa != null;
+ } catch (RemoteException e) {
+ mSyncEnabled = false;
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ }
+
+ private void setEmptyText() {
+ TextView empty = (TextView) findViewById(R.id.emptyText);
+ // Center the text by default
+ int gravity = Gravity.CENTER;
+ switch (mMode) {
+ case MODE_GROUP:
+ if (Groups.GROUP_MY_CONTACTS.equals(mDisplayInfo)) {
+ if (mSyncEnabled) {
+ empty.setText(getText(R.string.noContactsHelpTextWithSync));
+ } else {
+ empty.setText(getText(R.string.noContactsHelpText));
+ }
+ gravity = Gravity.NO_GRAVITY;
+ } 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;
+ }
+ empty.setGravity(gravity);
+ }
+
+ /**
+ * 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(PREF_DISPLAY_TYPE, DISPLAY_TYPE_UNKNOWN);
+ switch (mDisplayType) {
+ case DISPLAY_TYPE_ALL_WITH_PHONES: {
+ mMode = MODE_WITH_PHONES;
+ mDisplayInfo = null;
+ break;
+ }
+
+ case DISPLAY_TYPE_SYSTEM_GROUP: {
+ String systemId = prefs.getString(
+ 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 = DISPLAY_TYPE_ALL;
+ }
+ break;
+ }
+
+ case DISPLAY_TYPE_USER_GROUP: {
+ String displayGroup = prefs.getString(
+ 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 = DISPLAY_TYPE_ALL;
+ }
+ break;
+ }
+
+ case 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 = 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 (mJustCreated && runQuery) {
+ // We need to start a query here the first time the activity is launched, as long
+ // as we aren't doing a filter.
+ startQuery();
+ }
+ mJustCreated = false;
+ }
+
+ @Override
+ protected void onRestart() {
+ super.onRestart();
+
+ // The cursor was killed off in onStop(), so we need to get a new one here
+ startQuery();
+ }
+
+ 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 onRestart() 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;
+ }
+
+ // Search
+ menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
+ .setIcon(android.R.drawable.ic_menu_search);
+
+ // New contact
+ 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');
+
+ // Display group
+ if (mDefaultMode) {
+ menu.add(0, MENU_DISPLAY_GROUP, 0, R.string.menu_displayGroup)
+ .setIcon(com.android.internal.R.drawable.ic_menu_allfriends);
+ }
+
+ // Sync settings
+ if (mSyncEnabled) {
+ Intent syncIntent = new Intent(Intent.ACTION_VIEW);
+ syncIntent.setClass(this, ContactsGroupSyncSelector.class);
+ menu.add(0, 0, 0, R.string.syncGroupPreference)
+ .setIcon(com.android.internal.R.drawable.ic_menu_refresh)
+ .setIntent(syncIntent);
+ }
+
+ // SIM import
+ Intent importIntent = new Intent(Intent.ACTION_VIEW);
+ importIntent.setType("vnd.android.cursor.item/sim-contact");
+ importIntent.setClassName("com.android.phone", "com.android.phone.SimContacts");
+ menu.add(0, 0, 0, R.string.importFromSim)
+ .setIcon(R.drawable.ic_menu_import_contact)
+ .setIntent(importIntent);
+
+ 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 = DISPLAY_TYPE_ALL;
+ mDisplayInfo = null;
+ } else if (mDisplayGroupCurrentSelection
+ == DISPLAY_GROUP_INDEX_ALL_CONTACTS_WITH_PHONES) {
+ // Display all with phone numbers
+ mDisplayType = DISPLAY_TYPE_ALL_WITH_PHONES;
+ mDisplayInfo = null;
+ } else if (mDisplayGroupsIncludesMyContacts &&
+ mDisplayGroupCurrentSelection == DISPLAY_GROUP_INDEX_MY_CONTACTS) {
+ mDisplayType = DISPLAY_TYPE_SYSTEM_GROUP;
+ mDisplayInfo = Groups.GROUP_MY_CONTACTS;
+ } else {
+ mDisplayType = DISPLAY_TYPE_USER_GROUP;
+ mDisplayInfo = mDisplayGroups[mDisplayGroupCurrentSelection].toString();
+ }
+
+ // Save the changes to the preferences
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ prefs.edit()
+ .putInt(PREF_DISPLAY_TYPE, mDisplayType)
+ .putString(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) {
+ switch (item.getItemId()) {
+ case 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;
+
+ case MENU_SEARCH:
+ startSearch(null, false, null, false);
+ 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(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, new DeleteClickListener(uri))
+ .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(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, 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) {
+ // Hide soft keyboard, if visible
+ InputMethodManager inputMethodManager = (InputMethodManager)
+ getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.hideSoftInputFromWindow(mList.getWindowToken(), 0);
+
+ 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, 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, 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 == 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 static final class QueryHandler extends AsyncQueryHandler {
+ private final WeakReference<ContactsListActivity> mActivity;
+
+ public QueryHandler(Context context) {
+ super(context.getContentResolver());
+ mActivity = new WeakReference<ContactsListActivity>((ContactsListActivity) context);
+ }
+
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ final ContactsListActivity activity = mActivity.get();
+ if (activity != null && !activity.isFinishing()) {
+ activity.mAdapter.setLoading(false);
+ activity.getListView().clearTextFilter();
+ activity.mAdapter.changeCursor(cursor);
+
+ // Now that the cursor is populated again, it's possible to restore the list state
+ if (activity.mListState != null) {
+ activity.mList.onRestoreInstanceState(activity.mListState);
+ if (activity.mListHasFocus) {
+ activity.mList.requestFocus();
+ }
+ activity.mListHasFocus = false;
+ activity.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);
+ public ImageView presenceView;
+ }
+
+ private final class ContactItemListAdapter extends ResourceCursorAdapter
+ implements SectionIndexer {
+
+ private AlphabetIndexer mIndexer;
+ private String mAlphabet;
+ private boolean mLoading = true;
+ private CharSequence mUnknownNameText;
+ private CharSequence[] mLocalizedLabels;
+
+ public ContactItemListAdapter(Context context) {
+ super(context, R.layout.contacts_list_item, null, false);
+
+ mAlphabet = context.getString(com.android.internal.R.string.fast_scroll_alphabet);
+
+ 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;
+ }
+ }
+
+ /**
+ * Callback on the UI thread when the content observer on the backing cursor fires.
+ * Instead of calling requery we need to do an async query so that the requery doesn't
+ * block the UI thread for a long time.
+ */
+ @Override
+ protected void onContentChanged() {
+ CharSequence constraint = getListView().getTextFilter();
+ if (!TextUtils.isEmpty(constraint)) {
+ // Reset the filter state then start an async filter operation
+ Filter filter = getFilter();
+ filter.filter(constraint);
+ } else {
+ // Start an async query
+ startQuery();
+ }
+ }
+
+ public void setLoading(boolean loading) {
+ mLoading = loading;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ if ((mMode & MODE_MASK_CREATE_NEW) == MODE_MASK_CREATE_NEW) {
+ // This mode mask adds a header and we always want it to show up, even
+ // if the list is empty, so always claim the list is not empty.
+ return false;
+ } else {
+ if (mLoading) {
+ // We don't want the empty state to show when loading.
+ return false;
+ } else {
+ return super.isEmpty();
+ }
+ }
+ }
+
+ @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.numberView = (TextView) view.findViewById(R.id.number);
+ cache.presenceView = (ImageView) view.findViewById(R.id.presence);
+ 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;
+ TextView labelView = cache.labelView;
+ 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);
+ labelView.setVisibility(View.VISIBLE);
+ } else {
+ numberView.setVisibility(View.GONE);
+ labelView.setVisibility(View.GONE);
+ }
+
+ // Set the label
+ 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 {
+ // There is no label, hide the the view
+ labelView.setVisibility(View.GONE);
+ }
+
+ // Set the proper icon (star or presence or nothing)
+ ImageView presenceView = cache.presenceView;
+ 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);
+ presenceView.setImageResource(
+ Presence.getPresenceIconResourceId(serverStatus));
+ presenceView.setVisibility(View.VISIBLE);
+ } else {
+ presenceView.setVisibility(View.GONE);
+ }
+ } else {
+ presenceView.setVisibility(View.GONE);
+ }
+ } else {
+ if (cursor.getInt(STARRED_COLUMN_INDEX) != 0) {
+ presenceView.setImageResource(R.drawable.star_on);
+ presenceView.setVisibility(View.VISIBLE);
+ } else {
+ presenceView.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ @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 mIndexer.getSections();
+ }
+ }
+
+ 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.getPositionForSection(sectionIndex);
+ }
+
+ public int getSectionForPosition(int position) {
+ return 0;
+ }
+ }
+}
+
+
diff --git a/src/com/android/contacts/ContactsLiveFolders.java b/src/com/android/contacts/ContactsLiveFolders.java
new file mode 100644
index 000000000..8ca199a98
--- /dev/null
+++ b/src/com/android/contacts/ContactsLiveFolders.java
@@ -0,0 +1,112 @@
+/*
+ * 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.Intent;
+import android.content.Context;
+import android.net.Uri;
+import android.app.Activity;
+import android.os.Bundle;
+import android.provider.LiveFolders;
+import android.provider.Contacts;
+
+public class ContactsLiveFolders {
+ public static class StarredContacts extends Activity {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://contacts/live_folders/favorites");
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Intent intent = getIntent();
+ final String action = intent.getAction();
+
+ if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {
+ setResult(RESULT_OK, createLiveFolder(this, CONTENT_URI,
+ getString(R.string.liveFolder_favorites_label),
+ R.drawable.ic_launcher_contacts_starred));
+ } else {
+ setResult(RESULT_CANCELED);
+ }
+
+ finish();
+ }
+ }
+
+ public static class PhoneContacts extends Activity {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://contacts/live_folders/people_with_phones");
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Intent intent = getIntent();
+ final String action = intent.getAction();
+
+ if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {
+ setResult(RESULT_OK, createLiveFolder(this, CONTENT_URI,
+ getString(R.string.liveFolder_phones_label),
+ R.drawable.ic_launcher_contacts_phones));
+ } else {
+ setResult(RESULT_CANCELED);
+ }
+
+ finish();
+ }
+ }
+
+ public static class AllContacts extends Activity {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://contacts/live_folders/people");
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Intent intent = getIntent();
+ final String action = intent.getAction();
+
+ if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {
+ setResult(RESULT_OK, createLiveFolder(this, CONTENT_URI,
+ getString(R.string.liveFolder_all_label),
+ R.drawable.ic_launcher_contacts));
+ } else {
+ setResult(RESULT_CANCELED);
+ }
+
+ finish();
+ }
+ }
+
+ private static Intent createLiveFolder(Context context, Uri uri, String name,
+ int icon) {
+
+ final Intent intent = new Intent();
+
+ intent.setData(uri);
+ intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT, new Intent(Intent.ACTION_VIEW,
+ Contacts.People.CONTENT_URI));
+ intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, name);
+ intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON,
+ Intent.ShortcutIconResource.fromContext(context, icon));
+ intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE, LiveFolders.DISPLAY_MODE_LIST);
+
+ return intent;
+ }
+}
diff --git a/src/com/android/contacts/DialtactsActivity.java b/src/com/android/contacts/DialtactsActivity.java
new file mode 100644
index 000000000..d3f8981ff
--- /dev/null
+++ b/src/com/android/contacts/DialtactsActivity.java
@@ -0,0 +1,311 @@
+/*
+ * 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.Activity;
+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 implements TabHost.OnTabChangeListener {
+ 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();
+ mTabHost.setOnTabChangedListener(this);
+
+ // 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;
+ }
+
+ // Dismiss menu provided by any children activites
+ Activity activity = getLocalActivityManager().
+ getActivity(mTabHost.getCurrentTabTag());
+ if (activity != null) {
+ activity.closeOptionsMenu();
+ }
+
+ 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);
+ }
+
+ /** {@inheritDoc} */
+ public void onTabChanged(String tabId) {
+ // Because we're using Activities as our tab children, we trigger
+ // onWindowFocusChanged() to let them know when they're active. This may
+ // seem to duplicate the purpose of onResume(), but it's needed because
+ // onResume() can't reliably check if a keyguard is active.
+ Activity activity = getLocalActivityManager().getActivity(tabId);
+ if (activity != null) {
+ activity.onWindowFocusChanged(true);
+ }
+ }
+}
diff --git a/src/com/android/contacts/EditContactActivity.java b/src/com/android/contacts/EditContactActivity.java
new file mode 100644
index 000000000..49569e308
--- /dev/null
+++ b/src/com/android/contacts/EditContactActivity.java
@@ -0,0 +1,2108 @@
+/*
+ * 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_PHONETIC_NAME_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.ColorStateList;
+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.Groups;
+import android.provider.Contacts.Organizations;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.telephony.PhoneNumberFormattingTextWatcher;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.method.TextKeyListener;
+import android.text.method.TextKeyListener.Capitalize;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+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.view.inputmethod.EditorInfo;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+
+/**
+ * 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,
+ TextWatcher, View.OnFocusChangeListener {
+ 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;
+
+ // 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 DELETE_CONFIRMATION_DIALOG = 2;
+
+ // Section IDs
+ final static int SECTION_PHONES = 3;
+ final static int SECTION_EMAIL = 4;
+ final static int SECTION_IM = 5;
+ final static int SECTION_POSTAL = 6;
+ final static int SECTION_ORG = 7;
+ final static int SECTION_NOTE = 8;
+
+ // 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_PHOTO = 6;
+
+ /** 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 LinearLayout mLayout;
+ private LayoutInflater mInflater;
+ private MenuItem mPhotoMenuItem;
+ private boolean mPhotoPresent = false;
+ private EditText mPhoneticNameView; // invisible in some locales, but always present
+
+ /** Flag marking this contact as changed, meaning we should write changes back. */
+ private boolean mContactChanged = 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> mOrgEntries = new ArrayList<EditEntry>();
+ /* package */ ArrayList<EditEntry> mNoteEntries = 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;
+
+ private static final int[] TYPE_PRECEDENCE_PHONES = new int[] {
+ Phones.TYPE_MOBILE, Phones.TYPE_HOME, Phones.TYPE_WORK, Phones.TYPE_OTHER
+ };
+ private static final int[] TYPE_PRECEDENCE_METHODS = new int[] {
+ ContactMethods.TYPE_HOME, ContactMethods.TYPE_WORK, ContactMethods.TYPE_OTHER
+ };
+ private static final int[] TYPE_PRECEDENCE_IM = new int[] {
+ ContactMethods.PROTOCOL_GOOGLE_TALK, ContactMethods.PROTOCOL_AIM,
+ ContactMethods.PROTOCOL_MSN, ContactMethods.PROTOCOL_YAHOO,
+ ContactMethods.PROTOCOL_JABBER
+ };
+ private static final int[] TYPE_PRECEDENCE_ORG = new int[] {
+ Organizations.TYPE_WORK, Organizations.TYPE_OTHER
+ };
+
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.photoImage: {
+ doPickPhotoAction();
+ break;
+ }
+
+ case R.id.checkable: {
+ CheckBox checkBox = (CheckBox) v.findViewById(R.id.checkbox);
+ checkBox.toggle();
+
+ EditEntry entry = findEntryForView(v);
+ entry.data = checkBox.isChecked() ? "1" : "0";
+
+ mContactChanged = true;
+ break;
+ }
+
+ case R.id.entry_ringtone: {
+ EditEntry entry = findEntryForView(v);
+ doPickRingtone(entry);
+ break;
+ }
+
+ case R.id.separator: {
+ // Someone clicked on a section header, so handle add action
+ int sectionType = (Integer) v.getTag();
+ doAddAction(sectionType);
+ break;
+ }
+
+ case R.id.saveButton:
+ doSaveAction();
+ break;
+
+ case R.id.discardButton:
+ doRevertAction();
+ break;
+
+ case R.id.delete: {
+ 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;
+ }
+
+ // Force rebuild of views because section headers might need to change
+ buildViews();
+ 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;
+ }
+ }
+ }
+
+ private void setPhotoPresent(boolean present) {
+ mPhotoPresent = present;
+
+ // Correctly scale the contact photo if present, otherwise just center
+ // the photo placeholder icon.
+ if (mPhotoPresent) {
+ mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ } else {
+ mPhotoImageView.setImageResource(R.drawable.ic_menu_add_picture);
+ mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER);
+ }
+
+ 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(R.drawable.ic_menu_add_picture);
+ }
+ }
+ }
+
+ 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);
+ mPhoneticNameView = (EditText) findViewById(R.id.phonetic_name);
+
+ // Setup the bottom buttons
+ View 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();
+ }
+ setTitle(R.string.editContact_title_edit);
+ mState = STATE_EDIT;
+ } else if (action.equals(Intent.ACTION_INSERT)) {
+ if (icicle == null) {
+ // Build the entries & views
+ buildEntriesForInsert(getIntent().getExtras());
+ buildViews();
+ }
+ setTitle(R.string.editContact_title_insert);
+ 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(mOrgEntries);
+ mSections.add(mNoteEntries);
+ 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("orgEntries", mOrgEntries);
+ outState.putParcelableArrayList("noteEntries", mNoteEntries);
+ 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.putString("phoneticName", mPhoneticNameView.getText().toString());
+ outState.putBoolean("contactChanged", mContactChanged);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle inState) {
+ mPhoneEntries = inState.getParcelableArrayList("phoneEntries");
+ mEmailEntries = inState.getParcelableArrayList("emailEntries");
+ mImEntries = inState.getParcelableArrayList("imEntries");
+ mPostalEntries = inState.getParcelableArrayList("postalEntries");
+ mOrgEntries = inState.getParcelableArrayList("orgEntries");
+ mNoteEntries = inState.getParcelableArrayList("noteEntries");
+ 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");
+ mPhoneticNameView.setText(inState.getString("phoneticName"));
+ mContactChanged = inState.getBoolean("contactChanged");
+
+ // 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);
+ mContactChanged = true;
+ 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);
+ }
+
+ 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_PHOTO:
+ if (!mPhotoPresent) {
+ doPickPhotoAction();
+ } else {
+ doRemovePhotoAction();
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Try guessing the next-best type of {@link EditEntry} to insert into the
+ * given list. We walk down the precedence list until we find a type that
+ * doesn't exist yet, or default to the lowest ranking type.
+ */
+ private int guessNextType(ArrayList<EditEntry> entries, int[] precedenceList) {
+ // Keep track of the types we've seen already
+ SparseBooleanArray existAlready = new SparseBooleanArray(entries.size());
+ for (int i = entries.size() - 1; i >= 0; i--) {
+ EditEntry entry = entries.get(i);
+ if (!entry.isDeleted) {
+ existAlready.put(entry.type, true);
+ }
+ }
+
+ // Pick the first item we haven't seen
+ for (int type : precedenceList) {
+ if (!existAlready.get(type, false)) {
+ return type;
+ }
+ }
+
+ // Otherwise default to last item
+ return precedenceList[precedenceList.length - 1];
+ }
+
+ private void doAddAction(int sectionType) {
+ EditEntry entry = null;
+ switch (sectionType) {
+ case SECTION_PHONES: {
+ // Try figuring out which type to insert next
+ int nextType = guessNextType(mPhoneEntries, TYPE_PRECEDENCE_PHONES);
+ entry = EditEntry.newPhoneEntry(EditContactActivity.this,
+ Uri.withAppendedPath(mUri, People.Phones.CONTENT_DIRECTORY),
+ nextType);
+ mPhoneEntries.add(entry);
+ break;
+ }
+ case SECTION_EMAIL: {
+ // Try figuring out which type to insert next
+ int nextType = guessNextType(mEmailEntries, TYPE_PRECEDENCE_METHODS);
+ entry = EditEntry.newEmailEntry(EditContactActivity.this,
+ Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
+ nextType);
+ mEmailEntries.add(entry);
+ break;
+ }
+ case SECTION_IM: {
+ // Try figuring out which type to insert next
+ int nextType = guessNextType(mImEntries, TYPE_PRECEDENCE_IM);
+ entry = EditEntry.newImEntry(EditContactActivity.this,
+ Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
+ nextType);
+ mImEntries.add(entry);
+ break;
+ }
+ case SECTION_POSTAL: {
+ int nextType = guessNextType(mPostalEntries, TYPE_PRECEDENCE_METHODS);
+ entry = EditEntry.newPostalEntry(EditContactActivity.this,
+ Uri.withAppendedPath(mUri, People.ContactMethods.CONTENT_DIRECTORY),
+ nextType);
+ mPostalEntries.add(entry);
+ break;
+ }
+ case SECTION_ORG: {
+ int nextType = guessNextType(mOrgEntries, TYPE_PRECEDENCE_ORG);
+ entry = EditEntry.newOrganizationEntry(EditContactActivity.this,
+ Uri.withAppendedPath(mUri, Organizations.CONTENT_DIRECTORY),
+ nextType);
+ mOrgEntries.add(entry);
+ break;
+ }
+ case SECTION_NOTE: {
+ entry = EditEntry.newNotesEntry(EditContactActivity.this, null, mUri);
+ mNoteEntries.add(entry);
+ break;
+ }
+ }
+
+ // Rebuild the views if needed
+ if (entry != null) {
+ buildViews();
+ mContactChanged = true;
+
+ View dataView = entry.view.findViewById(R.id.data);
+ if (dataView == null) {
+ entry.view.requestFocus();
+ } else {
+ dataView.requestFocus();
+ }
+ }
+ }
+
+ 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(android.R.string.ok, null)
+ .show();
+ }
+ }
+
+ private void doRemovePhotoAction() {
+ mPhoto = null;
+ mPhotoChanged = true;
+ 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);
+
+ Uri ringtoneUri;
+ if (entry.data != null) {
+ ringtoneUri = Uri.parse(entry.data);
+ } else {
+ // Otherwise pick default ringtone Uri so that something is selected.
+ ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
+ }
+
+ // 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 = getOtherEntry(People.CUSTOM_RINGTONE);
+ 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) {
+ String ringtoneName;
+ if (entry.data == null) {
+ ringtoneName = 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;
+ }
+ ringtoneName = ringtone.getTitle(this);
+ }
+
+ updateDataView(entry, ringtoneName);
+ }
+
+ 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 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(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, 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;
+ }
+ }
+
+ private EditEntry getOtherEntry(String column) {
+ for (int i = mOtherEntries.size() - 1; i >= 0; i--) {
+ EditEntry entry = mOtherEntries.get(i);
+ if (isOtherEntry(entry, column)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ private static boolean isOtherEntry(EditEntry entry, String column) {
+ return entry != null && entry.column != null && entry.column.equals(column);
+ }
+
+ 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(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ entry.setLabel(EditContactActivity.this, ContactMethods.TYPE_CUSTOM,
+ label.getText().toString());
+ mContactChanged = true;
+
+ if (addTo != null) {
+ addTo.add(entry);
+ buildViews();
+ entry.view.requestFocus(View.FOCUS_DOWN);
+ }
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, 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.PHONETIC_NAME, mPhoneticNameView.getText().toString());
+ 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));
+
+ // Only notify user if we actually changed contact
+ if (mContactChanged || mPhotoChanged) {
+ 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.PHONETIC_NAME, mPhoneticNameView.getText().toString());
+
+ // 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(ContactsListActivity.PREF_DISPLAY_TYPE,
+ ContactsListActivity.DISPLAY_TYPE_UNKNOWN);
+ if (displayType == ContactsListActivity.DISPLAY_TYPE_USER_GROUP) {
+ String displayGroup = prefs.getString(ContactsListActivity.PREF_DISPLAY_INFO,
+ null);
+ if (!TextUtils.isEmpty(displayGroup)) {
+ People.addToGroup(mResolver, ContentUris.parseId(contactUri), displayGroup);
+ }
+ } else {
+ // Check to see if we're not syncing everything and if so if My Contacts is synced.
+ // If it isn't then the created contact can end up not in any groups that are
+ // currently synced and end up getting removed from the phone, which is really bad.
+ boolean syncingEverything = !"0".equals(Contacts.Settings.getSetting(mResolver, null,
+ Contacts.Settings.SYNC_EVERYTHING));
+ if (!syncingEverything) {
+ boolean syncingMyContacts = false;
+ Cursor c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups.SHOULD_SYNC },
+ Groups.SYSTEM_ID + "=?", new String[] { Groups.GROUP_MY_CONTACTS }, null);
+ if (c != null) {
+ try {
+ if (c.moveToFirst()) {
+ syncingMyContacts = !"0".equals(c.getString(0));
+ }
+ } finally {
+ c.close();
+ }
+ }
+
+ if (!syncingMyContacts) {
+ // Not syncing My Contacts, so find a group that is being synced and stick
+ // the contact in there. We sort the list so at least all contacts
+ // will appear in the same group.
+ c = mResolver.query(Groups.CONTENT_URI, new String[] { Groups._ID },
+ Groups.SHOULD_SYNC + "!=0", null, Groups.DEFAULT_SORT_ORDER);
+ if (c != null) {
+ try {
+ if (c.moveToFirst()) {
+ People.addToGroup(mResolver, ContentUris.parseId(contactUri),
+ c.getLong(0));
+ }
+ } finally {
+ c.close();
+ }
+ }
+ }
+ }
+ }
+
+ // 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));
+ mNameView.addTextChangedListener(this);
+
+ // Photo
+ mPhoto = People.loadContactPhoto(this, mUri, 0, null);
+ if (mPhoto == null) {
+ setPhotoPresent(false);
+ } else {
+ setPhotoPresent(true);
+ mPhotoImageView.setImageBitmap(mPhoto);
+ }
+
+ // 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;
+ mOrgEntries.add(entry);
+ }
+ organizationsCursor.close();
+ }
+
+ // Notes
+ if (!personCursor.isNull(CONTACT_NOTES_COLUMN)) {
+ entry = EditEntry.newNotesEntry(this, personCursor.getString(CONTACT_NOTES_COLUMN),
+ mUri);
+ mNoteEntries.add(entry);
+ }
+
+ // Ringtone
+ entry = EditEntry.newRingtoneEntry(this,
+ personCursor.getString(CONTACT_CUSTOM_RINGTONE_COLUMN), mUri);
+ mOtherEntries.add(entry);
+
+ // Send to voicemail
+ entry = EditEntry.newSendToVoicemailEntry(this,
+ personCursor.getString(CONTACT_SEND_TO_VOICEMAIL_COLUMN), mUri);
+ mOtherEntries.add(entry);
+
+ // Phonetic name
+ mPhoneticNameView.setText(personCursor.getString(CONTACT_PHONETIC_NAME_COLUMN));
+ mPhoneticNameView.addTextChangedListener(this);
+
+ 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);
+ }
+
+ mContactChanged = false;
+ }
+
+ /**
+ * 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);
+
+ // Send to voicemail
+ entry = EditEntry.newSendToVoicemailEntry(this, "0", 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);
+ }
+
+ // Read the phonetic name from the bundle
+ CharSequence phoneticName = extras.getCharSequence(Insert.PHONETIC_NAME);
+ if (!TextUtils.isEmpty(phoneticName)) {
+ mPhoneticNameView.setText(phoneticName);
+ }
+
+ // 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
+ addEmailFromExtras(extras, methodsUri, Insert.EMAIL, Insert.EMAIL_TYPE,
+ Insert.EMAIL_ISPRIMARY);
+ addEmailFromExtras(extras, methodsUri, Insert.SECONDARY_EMAIL, Insert.SECONDARY_EMAIL_TYPE,
+ null);
+ addEmailFromExtras(extras, methodsUri, Insert.TERTIARY_EMAIL, Insert.TERTIARY_EMAIL_TYPE,
+ null);
+
+ // Phone entries from extras
+ addPhoneFromExtras(extras, phonesUri, Insert.PHONE, Insert.PHONE_TYPE,
+ Insert.PHONE_ISPRIMARY);
+ addPhoneFromExtras(extras, phonesUri, Insert.SECONDARY_PHONE, Insert.SECONDARY_PHONE_TYPE,
+ null);
+ addPhoneFromExtras(extras, phonesUri, Insert.TERTIARY_PHONE, Insert.TERTIARY_PHONE_TYPE,
+ null);
+
+ // 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);
+ }
+ }
+
+ private void addEmailFromExtras(Bundle extras, Uri methodsUri, String emailField,
+ String typeField, String primaryField) {
+ CharSequence email = extras.getCharSequence(emailField);
+
+ // Correctly handle String in typeField as TYPE_CUSTOM
+ int emailType = INVALID_TYPE;
+ String customLabel = null;
+ if(extras.get(typeField) instanceof String) {
+ emailType = ContactMethods.TYPE_CUSTOM;
+ customLabel = extras.getString(typeField);
+ } else {
+ emailType = extras.getInt(typeField, INVALID_TYPE);
+ }
+
+ if (!TextUtils.isEmpty(email) && emailType == INVALID_TYPE) {
+ emailType = DEFAULT_EMAIL_TYPE;
+ mPrimaryEmailAdded = true;
+ }
+
+ if (emailType != INVALID_TYPE) {
+ EditEntry entry = EditEntry.newEmailEntry(this, customLabel, emailType, email.toString(),
+ methodsUri, 0);
+ entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
+ mEmailEntries.add(entry);
+
+ // Keep track of which primary types have been added
+ if (entry.isPrimary) {
+ mPrimaryEmailAdded = true;
+ }
+ }
+ }
+
+ private void addPhoneFromExtras(Bundle extras, Uri phonesUri, String phoneField,
+ String typeField, String primaryField) {
+ CharSequence phoneNumber = extras.getCharSequence(phoneField);
+
+ // Correctly handle String in typeField as TYPE_CUSTOM
+ int phoneType = INVALID_TYPE;
+ String customLabel = null;
+ if(extras.get(typeField) instanceof String) {
+ phoneType = Phones.TYPE_CUSTOM;
+ customLabel = extras.getString(typeField);
+ } else {
+ phoneType = extras.getInt(typeField, INVALID_TYPE);
+ }
+
+ if (!TextUtils.isEmpty(phoneNumber) && phoneType == INVALID_TYPE) {
+ phoneType = DEFAULT_PHONE_TYPE;
+ }
+
+ if (phoneType != INVALID_TYPE) {
+ EditEntry entry = EditEntry.newPhoneEntry(this, customLabel, phoneType,
+ phoneNumber.toString(), phonesUri, 0);
+ entry.isPrimary = (primaryField == null) ? false : extras.getBoolean(primaryField);
+ mPhoneEntries.add(entry);
+
+ // Keep track of which primary types have been added
+ if (phoneType == Phones.TYPE_MOBILE) {
+ mMobilePhoneAdded = true;
+ }
+ }
+ }
+
+ /**
+ * 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_edit, SECTION_PHONES);
+ buildViewsForSection(layout, mEmailEntries,
+ R.string.listSeparatorSendEmail_edit, SECTION_EMAIL);
+ buildViewsForSection(layout, mImEntries,
+ R.string.listSeparatorSendIm_edit, SECTION_IM);
+ buildViewsForSection(layout, mPostalEntries,
+ R.string.listSeparatorMapAddress_edit, SECTION_POSTAL);
+ buildViewsForSection(layout, mOrgEntries,
+ R.string.listSeparatorOrganizations, SECTION_ORG);
+ buildViewsForSection(layout, mNoteEntries,
+ R.string.label_notes, SECTION_NOTE);
+
+ buildOtherViews(layout, mOtherEntries);
+ }
+
+
+ /**
+ * 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, int sectionType) {
+
+ View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
+ layout.addView(divider);
+
+ // Count up undeleted children
+ int activeChildren = 0;
+ for (int i = section.size() - 1; i >= 0; i--) {
+ EditEntry entry = section.get(i);
+ if (!entry.isDeleted) {
+ activeChildren++;
+ }
+ }
+
+ // Build the correct group header based on undeleted children
+ ViewGroup header;
+ if (activeChildren == 0) {
+ header = (ViewGroup) mInflater.inflate(R.layout.edit_separator_alone, layout, false);
+ } else {
+ header = (ViewGroup) mInflater.inflate(R.layout.edit_separator, layout, false);
+ }
+
+ // Because we're emulating a ListView, we need to handle focus changes
+ // with some additional logic.
+ header.setOnFocusChangeListener(this);
+
+ TextView text = (TextView) header.findViewById(R.id.text);
+ text.setText(getText(separatorResource));
+
+ // Force TextView to always default color if we have children. This makes sure
+ // we don't change color when parent is pressed.
+ if (activeChildren > 0) {
+ ColorStateList stateList = text.getTextColors();
+ text.setTextColor(stateList.getDefaultColor());
+ }
+
+ View addView = header.findViewById(R.id.separator);
+ addView.setTag(Integer.valueOf(sectionType));
+ addView.setOnClickListener(this);
+
+ // 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);
+ header.addView(view);
+ }
+ }
+
+ layout.addView(header);
+ }
+
+ private void buildOtherViews(final LinearLayout layout, ArrayList<EditEntry> section) {
+ // Build views for the current section, putting a divider between each one
+ for (EditEntry entry : section) {
+ View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
+ layout.addView(divider);
+
+ entry.activity = this; // this could be null from when the state is restored
+ View view = buildViewForEntry(entry);
+ view.setOnClickListener(this);
+ layout.addView(view);
+ }
+
+ View divider = mInflater.inflate(R.layout.edit_divider, layout, false);
+ layout.addView(divider);
+ }
+
+ /**
+ * 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;
+
+ // Because we're emulating a ListView, we might need to handle focus changes
+ // with some additional logic.
+ if (entry.kind == Contacts.KIND_ORGANIZATION) {
+ view = mInflater.inflate(R.layout.edit_contact_entry_org, parent, false);
+ } else if (isOtherEntry(entry, People.CUSTOM_RINGTONE)) {
+ view = mInflater.inflate(R.layout.edit_contact_entry_ringtone, parent, false);
+ view.setOnFocusChangeListener(this);
+ } else if (isOtherEntry(entry, People.SEND_TO_VOICEMAIL)) {
+ view = mInflater.inflate(R.layout.edit_contact_entry_voicemail, parent, false);
+ view.setOnFocusChangeListener(this);
+ } 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);
+ }
+ }
+ int contentType = entry.contentType;
+ if (contentType != EditorInfo.TYPE_NULL) {
+ data.setInputType(contentType);
+ if (data2 != null) {
+ data2.setInputType(contentType);
+ }
+ if ((contentType&EditorInfo.TYPE_MASK_CLASS)
+ == EditorInfo.TYPE_CLASS_PHONE) {
+ data.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
+ if (data2 != null) {
+ data2.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
+ }
+ }
+ }
+
+ // Connect listeners up to watch for changed values.
+ if (data instanceof EditText) {
+ data.addTextChangedListener(this);
+ }
+ if (data2 instanceof EditText) {
+ data2.addTextChangedListener(this);
+ }
+
+ // Hook up the delete button
+ View delete = view.findViewById(R.id.delete);
+ if (delete != null) delete.setOnClickListener(this);
+
+ return view;
+ }
+
+ private void fillViewData(final EditEntry entry) {
+ if (isOtherEntry(entry, People.CUSTOM_RINGTONE)) {
+ updateRingtoneView(entry);
+ } else if (isOtherEntry(entry, People.SEND_TO_VOICEMAIL)) {
+ CheckBox checkBox = (CheckBox) entry.view.findViewById(R.id.checkbox);
+ boolean sendToVoicemail = false;
+ if (entry.data != null) {
+ sendToVoicemail = (Integer.valueOf(entry.data) == 1);
+ }
+ checkBox.setChecked(sendToVoicemail);
+ }
+ }
+
+ /**
+ * 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]);
+ mContactChanged = true;
+ }
+ } else {
+ mEntry.setLabel(EditContactActivity.this, which, mLabels[which]);
+ mContactChanged = true;
+ }
+ }
+ }
+
+ /**
+ * 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 contentType;
+ 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(contentType);
+ 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.contentType = 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;
+ }
+
+ // Only set the ISPRIMARY flag if part of the incoming data. This is because the
+ // ContentProvider will try finding a new primary when setting to false, meaning
+ // it's possible to lose primary altogether as we walk down the list. If this editor
+ // implements editing of primaries in the future, this will need to be revisited.
+ if (isPrimary) {
+ values.put(ContactMethods.ISPRIMARY, 1);
+ }
+
+ // 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.contentType = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_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.contentType = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
+ | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+ 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 send-to-voicemail entry with the given data.
+ */
+ public static final EditEntry newSendToVoicemailEntry(EditContactActivity activity,
+ String data, Uri uri) {
+ EditEntry entry = new EditEntry(activity);
+ entry.label = activity.getString(R.string.actionIncomingCall);
+ entry.data = data;
+ entry.uri = uri;
+ entry.column = People.SEND_TO_VOICEMAIL;
+ 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.contentType = EditorInfo.TYPE_CLASS_PHONE;
+ 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.contentType = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
+ 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.contentType = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
+ | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+ entry.maxLines = 4;
+ entry.lines = 2;
+ return entry;
+ }
+
+ /**
+ * Create a new IM address entry
+ */
+ public static final EditEntry newImEntry(EditContactActivity activity,
+ Uri uri, int type) {
+ return newImEntry(activity, null, type, null, uri, 0);
+ }
+
+ /**
+ * Create a new IM 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.contentType = EditorInfo.TYPE_CLASS_TEXT;
+ return entry;
+ }
+ }
+
+ public void afterTextChanged(Editable s) {
+ // Someone edited a text field, so assume this contact is changed
+ mContactChanged = true;
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // Do nothing; editing handled by afterTextChanged()
+ }
+
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // Do nothing; editing handled by afterTextChanged()
+ }
+
+ public void onFocusChange(View v, boolean hasFocus) {
+ // Because we're emulating a ListView, we need to setSelected() for
+ // views as they are focused.
+ v.setSelected(hasFocus);
+ }
+}
diff --git a/src/com/android/contacts/RecentCallsListActivity.java b/src/com/android/contacts/RecentCallsListActivity.java
new file mode 100644
index 000000000..dbf287987
--- /dev/null
+++ b/src/com/android/contacts/RecentCallsListActivity.java
@@ -0,0 +1,828 @@
+/*
+ * 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.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.Contacts.Intents.Insert;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+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;
+import java.lang.ref.WeakReference;
+
+/**
+ * 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.DISPLAY_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 final LinkedList<CallerInfoQuery> mRequests;
+ private volatile 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;
+ private Thread mCallerIdThread;
+
+ 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;
+ mCallerIdThread = new Thread(this);
+ mCallerIdThread.setPriority(Thread.MIN_PRIORITY);
+ mCallerIdThread.start();
+ }
+
+ public void stopRequestProcessing() {
+ mDone = true;
+ if (mCallerIdThread != null) mCallerIdThread.interrupt();
+ }
+
+ 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,
+ Uri.encode(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);
+ } else {
+ // Just a raw number, format it to look pretty
+ number = PhoneNumberUtils.formatNumber(number);
+ }
+
+ 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 date/time field by mixing relative and absolute times.
+ int flags = DateUtils.FORMAT_ABBREV_RELATIVE | DateUtils.FORMAT_SHOW_DATE
+ | DateUtils.FORMAT_ABBREV_MONTH;
+
+ views.dateView.setText(DateUtils.getRelativeDateTimeString(context, date,
+ DateUtils.MINUTE_IN_MILLIS, DateUtils.DAY_IN_MILLIS * 2, flags));
+
+ // 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 static final class QueryHandler extends AsyncQueryHandler {
+ private final WeakReference<RecentCallsListActivity> mActivity;
+
+ public QueryHandler(Context context) {
+ super(context.getContentResolver());
+ mActivity = new WeakReference<RecentCallsListActivity>(
+ (RecentCallsListActivity) context);
+ }
+
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ final RecentCallsListActivity activity = mActivity.get();
+ if (activity != null && !activity.isFinishing()) {
+ final RecentCallsListActivity.RecentCallsAdapter callsAdapter = activity.mAdapter;
+ callsAdapter.setLoading(false);
+ callsAdapter.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();
+
+ 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();
+ mAdapter.stopRequestProcessing();
+ Cursor cursor = mAdapter.getCursor();
+ if (cursor != null && !cursor.isClosed()) {
+ cursor.close();
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+
+ // Clear notifications only when window gains focus. This activity won't
+ // immediately receive focus if the keyguard screen is above it.
+ if (hasFocus) {
+ try {
+ ITelephony iTelephony =
+ ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
+ if (iTelephony != null) {
+ iTelephony.cancelMissedCallsNotification();
+ } else {
+ Log.w(TAG, "Telephony service is null, can't call " +
+ "cancelMissedCallsNotification");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to clear missed calls notification due to remote exception");
+ }
+ }
+ }
+
+ 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) {
+ Intent intent = new Intent(this, CallDetailActivity.class);
+ intent.setData(ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI, id));
+ startActivity(intent);
+ }
+}
diff --git a/src/com/android/contacts/SpecialCharSequenceMgr.java b/src/com/android/contacts/SpecialCharSequenceMgr.java
new file mode 100644
index 000000000..38bc93dc6
--- /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(android.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 000000000..c80e67b8f
--- /dev/null
+++ b/src/com/android/contacts/TwelveKeyDialer.java
@@ -0,0 +1,913 @@
+/*
+ * 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.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+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.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.provider.Contacts.Intents.Insert;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.Contacts.PhonesColumns;
+import android.provider.Settings;
+import android.telephony.PhoneNumberFormattingTextWatcher;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.method.DialerKeyListener;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.android.internal.telephony.ITelephony;
+
+/**
+ * Dialer activity that displays the typical twelve key interface.
+ */
+public class TwelveKeyDialer extends Activity implements View.OnClickListener,
+ View.OnLongClickListener, View.OnKeyListener,
+ AdapterView.OnItemClickListener, 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;
+ private View mDigitsAndBackspace;
+ private View mDialpad;
+ private ListView mDialpadChooser;
+ private DialpadChooserAdapter mDialpadChooserAdapter;
+
+ // 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;
+
+ PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ /**
+ * Listen for phone state changes so that we can take down the
+ * "dialpad chooser" if the phone becomes idle while the
+ * chooser UI is visible.
+ */
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
+ // + state + ", '" + incomingNumber + "'");
+ if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
+ // Log.i(TAG, "Call ended with dialpad chooser visible! Taking it down...");
+ // Note there's a race condition in the UI here: the
+ // dialpad chooser could conceivably disappear (on its
+ // own) at the exact moment the user was trying to select
+ // one of the choices, which would be confusing. (But at
+ // least that's better than leaving the dialpad chooser
+ // onscreen, but useless...)
+ showDialpadChooser(false);
+ }
+ }
+ };
+
+ 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;
+
+ mDigitsAndBackspace = (View) findViewById(R.id.digitsAndBackspace);
+ mDialpad = (View) findViewById(R.id.dialpad); // This is null in landscape mode
+
+ // Set up the "dialpad chooser" UI; see showDialpadChooser().
+ mDialpadChooser = (ListView) findViewById(R.id.dialpadChooser);
+ mDialpadChooser.setOnItemClickListener(this);
+
+ 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_VOICE_CALL,
+ 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();
+ }
+ // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
+
+ // by default we are not adding a call.
+ mIsAddCallMode = false;
+
+ // By default we don't show the "dialpad chooser" UI.
+ boolean needToShowDialpadChooser = 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();
+ }
+ }
+ }
+ }
+ } else if (Intent.ACTION_MAIN.equals(action)) {
+ // The MAIN action means we're bringing up a blank dialer
+ // (e.g. by selecting the Home shortcut, or tabbing over from
+ // Contacts or Call log.)
+ //
+ // At this point, IF there's already an active call, there's a
+ // good chance that the user got here accidentally (but really
+ // wanted the in-call dialpad instead). So we bring up an
+ // intermediate UI to make the user confirm what they really
+ // want to do.
+ if (phoneIsInUse()) {
+ // Log.i(TAG, "resolveIntent(): phone is in use; showing dialpad chooser!");
+ needToShowDialpadChooser = true;
+ }
+ }
+
+ // Bring up the "dialpad chooser" IFF we need to make the user
+ // confirm which dialpad they really want.
+ showDialpadChooser(needToShowDialpadChooser);
+
+ 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_VOICE_CALL,
+ 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();
+ }
+ }
+
+ // While we're in the foreground, listen for phone state changes,
+ // purely so that we can take down the "dialpad chooser" if the
+ // phone becomes idle while the chooser UI is visible.
+ TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
+ telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+
+ // Potentially show hint text in the mDigits field when the user
+ // hasn't typed any digits yet. (If there's already an active call,
+ // this hint text will remind the user that he's about to add a new
+ // call.)
+ //
+ // TODO: consider adding better UI for the case where *both* lines
+ // are currently in use. (Right now we let the user try to add
+ // another call, but that call is guaranteed to fail. Perhaps the
+ // entire dialer UI should be disabled instead.)
+ if (phoneIsInUse()) {
+ mDigits.setHint(R.string.dialerDialpadHintText);
+ } else {
+ // Common case; no hint necessary.
+ mDigits.setHint(null);
+
+ // Also, a sanity-check: the "dialpad chooser" UI should NEVER
+ // be visible if the phone is idle!
+ showDialpadChooser(false);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ // Stop listening for phone state changes.
+ TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
+ telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+
+ 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) {
+ // We never show a menu if the "choose dialpad" UI is up.
+ if (dialpadChooserVisible()) {
+ return false;
+ }
+
+ 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);
+ }
+ }
+
+ /**
+ * Brings up the "dialpad chooser" UI in place of the usual Dialer
+ * elements (the textfield/button and the dialpad underneath).
+ *
+ * We show this UI if the user brings up the Dialer while a call is
+ * already in progress, since there's a good chance we got here
+ * accidentally (and the user really wanted the in-call dialpad instead).
+ * So in this situation we display an intermediate UI that lets the user
+ * explicitly choose between the in-call dialpad ("Use touch tone
+ * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
+ * to call in progress" just goes back to the in-call UI with no dialpad
+ * at all.)
+ *
+ * @param enabled If true, show the "dialpad chooser" instead
+ * of the regular Dialer UI
+ */
+ private void showDialpadChooser(boolean enabled) {
+ if (enabled) {
+ // Log.i(TAG, "Showing dialpad chooser!");
+ mDigitsAndBackspace.setVisibility(View.GONE);
+ if (mDialpad != null) mDialpad.setVisibility(View.GONE);
+ mDialpadChooser.setVisibility(View.VISIBLE);
+
+ // Instantiate the DialpadChooserAdapter and hook it up to the
+ // ListView. We do this only once.
+ if (mDialpadChooserAdapter == null) {
+ mDialpadChooserAdapter = new DialpadChooserAdapter(this);
+ mDialpadChooser.setAdapter(mDialpadChooserAdapter);
+ }
+ } else {
+ // Log.i(TAG, "Displaying normal Dialer UI.");
+ mDigitsAndBackspace.setVisibility(View.VISIBLE);
+ if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
+ mDialpadChooser.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * @return true if we're currently showing the "dialpad chooser" UI.
+ */
+ private boolean dialpadChooserVisible() {
+ return mDialpadChooser.getVisibility() == View.VISIBLE;
+ }
+
+ /**
+ * Simple list adapter, binding to an icon + text label
+ * for each item in the "dialpad chooser" list.
+ */
+ private static class DialpadChooserAdapter extends BaseAdapter {
+ private LayoutInflater mInflater;
+
+ // Simple struct for a single "choice" item.
+ static class ChoiceItem {
+ String text;
+ Bitmap icon;
+ int id;
+
+ public ChoiceItem(String s, Bitmap b, int i) {
+ text = s;
+ icon = b;
+ id = i;
+ }
+ }
+
+ // IDs for the possible "choices":
+ static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
+ static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
+ static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
+
+ private static final int NUM_ITEMS = 3;
+ private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
+
+ public DialpadChooserAdapter(Context context) {
+ // Cache the LayoutInflate to avoid asking for a new one each time.
+ mInflater = LayoutInflater.from(context);
+
+ // Initialize the possible choices.
+ // TODO: could this be specified entirely in XML?
+
+ // - "Use touch tone keypad"
+ mChoiceItems[0] = new ChoiceItem(
+ context.getString(R.string.dialer_useDtmfDialpad),
+ BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.ic_dialer_fork_tt_keypad),
+ DIALPAD_CHOICE_USE_DTMF_DIALPAD);
+
+ // - "Return to call in progress"
+ mChoiceItems[1] = new ChoiceItem(
+ context.getString(R.string.dialer_returnToInCallScreen),
+ BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.ic_dialer_fork_current_call),
+ DIALPAD_CHOICE_RETURN_TO_CALL);
+
+ // - "Add call"
+ mChoiceItems[2] = new ChoiceItem(
+ context.getString(R.string.dialer_addAnotherCall),
+ BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.ic_dialer_fork_add_call),
+ DIALPAD_CHOICE_ADD_NEW_CALL);
+ }
+
+ public int getCount() {
+ return NUM_ITEMS;
+ }
+
+ /**
+ * Return the ChoiceItem for a given position.
+ */
+ public Object getItem(int position) {
+ return mChoiceItems[position];
+ }
+
+ /**
+ * Return a unique ID for each possible choice.
+ */
+ public long getItemId(int position) {
+ return position;
+ }
+
+ /**
+ * Make a view for each row.
+ */
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // When convertView is non-null, we can reuse it (there's no need
+ // to reinflate it.)
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
+ }
+
+ TextView text = (TextView) convertView.findViewById(R.id.text);
+ text.setText(mChoiceItems[position].text);
+
+ ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
+ icon.setImageBitmap(mChoiceItems[position].icon);
+
+ return convertView;
+ }
+ }
+
+ /**
+ * Handle clicks from the dialpad chooser.
+ */
+ public void onItemClick(AdapterView parent, View v, int position, long id) {
+ DialpadChooserAdapter.ChoiceItem item =
+ (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
+ int itemId = item.id;
+ switch (itemId) {
+ case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
+ // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
+ // Fire off an intent to go back to the in-call UI
+ // with the dialpad visible.
+ returnToInCallScreen(true);
+ break;
+
+ case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
+ // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
+ // Fire off an intent to go back to the in-call UI
+ // (with the dialpad hidden).
+ returnToInCallScreen(false);
+ break;
+
+ case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
+ // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
+ // Ok, guess the user really did want to be here (in the
+ // regular Dialer) after all. Bring back the normal Dialer UI.
+ showDialpadChooser(false);
+ break;
+
+ default:
+ Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
+ break;
+ }
+ }
+
+ /**
+ * Returns to the in-call UI (where there's presumably a call in
+ * progress) in response to the user selecting "use touch tone keypad"
+ * or "return to call" from the dialpad chooser.
+ */
+ private void returnToInCallScreen(boolean showDialpad) {
+ try {
+ ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+ if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
+ } catch (RemoteException e) {
+ Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
+ }
+
+ // Finally, finish() ourselves so that we don't stay on the
+ // activity stack.
+ // Note that we do this whether or not the showCallScreenWithDialpad()
+ // call above had any effect or not! (That call is a no-op if the
+ // phone is idle, which can happen if the current call ends while
+ // the dialpad chooser is up. In this case we can't show the
+ // InCallScreen, and there's no point staying here in the Dialer,
+ // so we just take the user back where he came from...)
+ finish();
+ }
+
+ /**
+ * @return true if the phone is "in use", meaning that at least one line
+ * is active (ie. off hook or ringing or dialing).
+ */
+ private boolean phoneIsInUse() {
+ boolean phoneInUse = false;
+ try {
+ ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+ if (phone != null) phoneInUse = !phone.isIdle();
+ } catch (RemoteException e) {
+ Log.w(TAG, "phone.isIdle() failed", e);
+ }
+ return phoneInUse;
+ }
+}
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
new file mode 100644
index 000000000..99e72b0c7
--- /dev/null
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -0,0 +1,1032 @@
+/*
+ * 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_PHONETIC_NAME_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.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+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;
+import java.util.List;
+
+/**
+ * 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 String SHOW_BARCODE_INTENT = "com.google.zxing.client.android.ENCODE";
+
+ private static final boolean SHOW_SEPARATORS = false;
+
+ private static final String[] PHONE_KEYS = {
+ Contacts.Intents.Insert.PHONE,
+ Contacts.Intents.Insert.SECONDARY_PHONE,
+ Contacts.Intents.Insert.TERTIARY_PHONE
+ };
+
+ private static final String[] EMAIL_KEYS = {
+ Contacts.Intents.Insert.EMAIL,
+ Contacts.Intents.Insert.SECONDARY_EMAIL,
+ Contacts.Intents.Insert.TERTIARY_EMAIL
+ };
+
+ 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;
+ public static final int MENU_ITEM_SHOW_BARCODE = 3;
+
+ 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 TextView mPhoneticNameView; // may be null in some locales
+ 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);
+ mPhoneticNameView = (TextView) findViewById(R.id.phonetic_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(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, 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);
+ }
+
+ if (mPhoneticNameView != null) {
+ String phoneticName = mCursor.getString(CONTACT_PHONETIC_NAME_COLUMN);
+ mPhoneticNameView.setText(phoneticName);
+ }
+
+ // 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, SHOW_SEPARATORS);
+ }
+ } 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 boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ // Perform this check each time the menu is about to be shown, because the Barcode Scanner
+ // could be installed or uninstalled at any time.
+ if (isBarcodeScannerInstalled()) {
+ if (menu.findItem(MENU_ITEM_SHOW_BARCODE) == null) {
+ menu.add(0, MENU_ITEM_SHOW_BARCODE, 0, R.string.menu_showBarcode)
+ .setIcon(R.drawable.ic_menu_show_barcode);
+ }
+ } else {
+ menu.removeItem(MENU_ITEM_SHOW_BARCODE);
+ }
+ return true;
+ }
+
+ private boolean isBarcodeScannerInstalled() {
+ final Intent intent = new Intent(SHOW_BARCODE_INTENT);
+ ResolveInfo ri = getPackageManager().resolveActivity(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return ri != null;
+ }
+
+ @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, SHOW_SEPARATORS);
+ 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;
+ }
+ case MENU_ITEM_SHOW_BARCODE:
+ if (mCursor.moveToFirst()) {
+ Intent intent = new Intent(SHOW_BARCODE_INTENT);
+ intent.putExtra("ENCODE_TYPE", "CONTACT_TYPE");
+ Bundle bundle = new Bundle();
+ String name = mCursor.getString(CONTACT_NAME_COLUMN);
+ if (!TextUtils.isEmpty(name)) {
+ bundle.putString(Contacts.Intents.Insert.NAME, name);
+ // The 0th ViewEntry in each ArrayList below is a separator item
+ int entriesToAdd = Math.min(mPhoneEntries.size() - 1, PHONE_KEYS.length);
+ for (int x = 0; x < entriesToAdd; x++) {
+ ViewEntry entry = mPhoneEntries.get(x + 1);
+ bundle.putString(PHONE_KEYS[x], entry.data);
+ }
+ entriesToAdd = Math.min(mEmailEntries.size() - 1, EMAIL_KEYS.length);
+ for (int x = 0; x < entriesToAdd; x++) {
+ ViewEntry entry = mEmailEntries.get(x + 1);
+ bundle.putString(EMAIL_KEYS[x], entry.data);
+ }
+ if (mPostalEntries.size() >= 2) {
+ ViewEntry entry = mPostalEntries.get(1);
+ bundle.putString(Contacts.Intents.Insert.POSTAL, entry.data);
+ }
+ intent.putExtra("ENCODE_DATA", bundle);
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ // The check in onPrepareOptionsMenu() should make this impossible, but
+ // for safety I'm catching the exception rather than crashing. Ideally
+ // I'd call Menu.removeItem() here too, but I don't see a way to get
+ // the options menu.
+ Log.e(TAG, "Show barcode menu item was clicked but Barcode Scanner " +
+ "was not installed.");
+ }
+ return true;
+ }
+ }
+ break;
+ }
+ 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,
+ SHOW_SEPARATORS);
+ 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, SHOW_SEPARATORS);
+ 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, SHOW_SEPARATORS);
+ 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);
+ }
+
+ private Uri constructImToUrl(String host, String data) {
+ // don't encode the url, because the Activity Manager can't find using the encoded url
+ StringBuilder buf = new StringBuilder("imto://");
+ buf.append(host);
+ buf.append('/');
+ buf.append(data);
+ return Uri.parse(buf.toString());
+ }
+
+ /**
+ * 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();
+ }
+
+ if (SHOW_SEPARATORS) {
+ 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();
+ final CharSequence displayLabel = Phones.getDisplayLabel(this, type, label);
+ entry.label = buildActionString(R.string.actionCall, displayLabel, true);
+ 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 = android.R.drawable.sym_action_call;
+ mPhoneEntries.add(entry);
+
+ if (type == Phones.TYPE_MOBILE || mShowSmsLinksForAllPhones) {
+ // Add an SMS entry
+ ViewEntry smsEntry = new ViewEntry();
+ smsEntry.label = buildActionString(R.string.actionText, displayLabel, true);
+ 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 = buildActionString(R.string.actionEmail,
+ ContactMethods.getDisplayLabel(this, kind, type, label), true);
+ entry.data = data;
+ entry.intent = new Intent(Intent.ACTION_SENDTO,
+ Uri.fromParts("mailto", data, null));
+ entry.actionIcon = android.R.drawable.sym_action_email;
+ mEmailEntries.add(entry);
+ break;
+
+ case Contacts.KIND_POSTAL:
+ entry.label = buildActionString(R.string.actionMap,
+ ContactMethods.getDisplayLabel(this, kind, type, label), true);
+ 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 host;
+ if (protocolObj instanceof Number) {
+ int protocol = ((Number) protocolObj).intValue();
+ entry.label = buildActionString(R.string.actionChat,
+ protocolStrings[protocol], false);
+ host = ContactMethods.lookupProviderNameFromId(protocol).toLowerCase();
+ if (protocol == ContactMethods.PROTOCOL_GOOGLE_TALK
+ || protocol == ContactMethods.PROTOCOL_MSN) {
+ entry.maxLabelLines = 2;
+ }
+ } else {
+ String providerName = (String) protocolObj;
+ entry.label = buildActionString(R.string.actionChat,
+ providerName, false);
+ host = providerName.toLowerCase();
+ }
+
+ // Only add the intent if there is a valid host
+ if (!TextUtils.isEmpty(host)) {
+ entry.intent = new Intent(Intent.ACTION_SENDTO,
+ constructImToUrl(host, data));
+ }
+ entry.data = data;
+ if (!methodsCursor.isNull(METHODS_STATUS_COLUMN)) {
+ entry.presenceIcon = Presence.getPresenceIconResourceId(
+ methodsCursor.getInt(METHODS_STATUS_COLUMN));
+ }
+ entry.actionIcon = android.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 host;
+ if (protocolObj instanceof Number) {
+ int protocol = ((Number) protocolObj).intValue();
+ label = getResources().getStringArray(
+ android.R.array.imProtocols)[protocol];
+ host = ContactMethods.lookupProviderNameFromId(protocol).toLowerCase();
+ } else {
+ String providerName = (String) protocolObj;
+ label = providerName;
+ host = providerName.toLowerCase();
+ }
+
+ if (TextUtils.isEmpty(host)) {
+ // A valid provider name is required
+ continue;
+ }
+
+
+ Intent intent = new Intent(Intent.ACTION_SENDTO, constructImToUrl(host, data));
+
+ // 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 = android.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.label = organizationsCursor.getString(ORGANIZATIONS_COMPANY_COLUMN);
+ entry.data = organizationsCursor.getString(ORGANIZATIONS_TITLE_COLUMN);
+ entry.actionIcon = R.drawable.sym_action_organization;
+/*
+ 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;
+ entry.actionIcon = R.drawable.sym_note;
+ 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;
+ entry.actionIcon = R.drawable.sym_ringtone;
+ mOtherEntries.add(entry);
+ }
+ }
+ }
+
+ // Build the send directly to voice mail entry
+ boolean sendToVoicemail = personCursor.getInt(CONTACT_SEND_TO_VOICEMAIL_COLUMN) == 1;
+ if (sendToVoicemail) {
+ ViewEntry entry = new ViewEntry();
+ entry.label = getString(R.string.actionIncomingCall);
+ entry.data = getString(R.string.detailIncomingCallsGoToVoicemail);
+ entry.kind = ViewEntry.KIND_CONTACT;
+ entry.actionIcon = R.drawable.sym_send_to_voicemail;
+ mOtherEntries.add(entry);
+ }
+ }
+
+ String buildActionString(int actionResId, CharSequence type, boolean lowerCase) {
+ if (lowerCase) {
+ return getString(actionResId, type.toString().toLowerCase());
+ } else {
+ return getString(actionResId, type.toString());
+ }
+ }
+
+ /**
+ * A basic structure with the data for a contact entry in the list.
+ */
+ 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 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 ImageView actionIcon;
+ public ImageView presenceIcon;
+
+ // Need to keep track of this too
+ ViewEntry entry;
+ }
+
+ ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
+ super(context, sections, SHOW_SEPARATORS);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewEntry entry = getEntry(mSections, position, false);
+ View v;
+
+ // Handle separators specially
+ if (entry.kind == ViewEntry.KIND_SEPARATOR) {
+ TextView separator = (TextView) mInflater.inflate(
+ R.layout.list_separator, parent, SHOW_SEPARATORS);
+ separator.setText(entry.data);
+ return separator;
+ }
+
+ ViewCache views;
+
+ // Check to see if we can reuse convertView
+ if (convertView != null) {
+ v = convertView;
+ views = (ViewCache) v.getTag();
+ } else {
+ // Create a new view if needed
+ v = mInflater.inflate(R.layout.list_item_text_icons, parent, false);
+
+ // Cache the children
+ views = new ViewCache();
+ views.label = (TextView) v.findViewById(android.R.id.text1);
+ views.data = (TextView) v.findViewById(android.R.id.text2);
+ views.actionIcon = (ImageView) v.findViewById(R.id.icon1);
+ views.presenceIcon = (ImageView) v.findViewById(R.id.icon2);
+ v.setTag(views);
+ }
+
+ // Update the entry in the view cache
+ views.entry = entry;
+
+ // 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 action icon
+ ImageView action = views.actionIcon;
+ if (entry.actionIcon != -1) {
+ action.setImageDrawable(resources.getDrawable(entry.actionIcon));
+ action.setVisibility(View.VISIBLE);
+ } else {
+ // Things should still line up as if there was an icon, so make it invisible
+ action.setVisibility(View.INVISIBLE);
+ }
+
+ // Set the presence icon
+ Drawable presenceIcon = null;
+ if (entry.primaryIcon != -1) {
+ presenceIcon = resources.getDrawable(entry.primaryIcon);
+ } else if (entry.presenceIcon != -1) {
+ presenceIcon = resources.getDrawable(entry.presenceIcon);
+ }
+
+ ImageView presence = views.presenceIcon;
+ if (presenceIcon != null) {
+ presence.setImageDrawable(presenceIcon);
+ presence.setVisibility(View.VISIBLE);
+ } else {
+ presence.setVisibility(View.GONE);
+ }
+ }
+
+ 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 000000000..47782e398
--- /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 000000000..ca28a6aaf
--- /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 000000000..85dba56bc
--- /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);
+ }
+}